converting socket process to threads c - c

I have this program that i can run with a port number then in browser do something like localhost:port/image.jpg and it will open the image. However i want to try and do this using threads.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#define BufferSize 1024 // for parsing request
#define BIG_ENUF 4096 // For request header
void error(char *); // prototype for quick error exit
int main(int argc, char *argv[]) // arv[1] has port #
{
int sockfd, newsockfd, portno, clilen,Connect_Count=0;
char buffer[BufferSize]; // for communicating with client
char * BigBuffer; // for serving file to client
int BufferNdx,n ;// workaday subscripts
char * TmpBuffer, *SavePtr, *FileName, *GetToken;
pid_t pid; // for forks;
FILE * F; // for streaming file when GET served
struct stat S;// to find file length
struct sockaddr_in serv_addr, cli_addr;
// structs for client and server
if (argc < 2) { // looking for port #
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
// specifies TCP IP flow
if (sockfd < 0)error("ERROR opening socket");
memset( (char *) &serv_addr, 0, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno); // proper byte order
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR on binding");
GetToken = strtok_r(TmpBuffer," ",&SavePtr);
// And Now port is bound to socket for TCP / IP
while (Connect_Count < 10) // Limit on Number of Connections
{listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
// listen blocks until someone knocks and when we accept
// the client structure is filled by accept
if (newsockfd < 0) // exit server if bad accept
error( "ERROR on accept");
// otherwise let's fork a process to do that work
pid = fork();
if (pid < 0) error("bad fork\n");
if (pid == 0) break;
// to handle talking with client below
//if parent, loop again to listen if more connections ?
}
// ===================================================
// Forked child
memset(buffer, 0,BufferSize);
n = read(newsockfd,buffer,BufferSize-1); // This leaves null at end
if (n < 0) error("ERROR reading from socket");
printf("%s\n",(TmpBuffer=strtok_r(buffer,"\n",&SavePtr)));
GetToken = strtok_r(TmpBuffer," ",&SavePtr);
printf("%s\n",GetToken);
GetToken = strtok_r(NULL," ",&SavePtr);
printf("%s After Get\n",GetToken); // file name token begins '/'
GetToken++; // Point to first character of actual file name
// now open the file and send it to client ?
if ((F = fopen(GetToken,"r")) == NULL) error("Bad\n");
else printf("Good\n");
int FileSize;
if ((fstat(fileno(F),&S)==-1)) error("failed fstat\n"); // Need file size
FileSize = S.st_size;
char Response[BIG_ENUF];int HeaderCount=0;
HeaderCount=0;//Use to know where to fill buffer with sprintf
HeaderCount+=sprintf( Response+HeaderCount,"HTTP/1.0 200 OK\r\n");
HeaderCount+=sprintf( Response+HeaderCount,"Server: Flaky Server/1.0.0\r\n");
HeaderCount+=sprintf( Response+HeaderCount,"Content-Type: image/jpeg\r\n");
HeaderCount+=sprintf( Response+HeaderCount,"Content-Length:%d\r\n",FileSize);
//delimit header
HeaderCount+=sprintf( Response+HeaderCount,"\r\n");
fprintf(stderr,"HeaderCount %d and Header\n",HeaderCount);
write(STDERR_FILENO, Response, HeaderCount);
write(newsockfd,Response,HeaderCount); // and send to client
BigBuffer = malloc(FileSize+2);
fread(BigBuffer,1,FileSize,F);
write(newsockfd,BigBuffer,FileSize);
free(BigBuffer);
// Now close up this client
close(newsockfd);
return 0;
}
// bad error routine
void error(char *msg)
{
perror(msg);
exit(1);
}
I'm trying to convert these processes into a threads program. I'm very new to threads. But is the basic concept to move everything i have labeled under the child process to the thread function? and replace the forking with pthread_create s?

Related

socket programming server in C

I'm currently learning socket programming in C on linux system. I got a sample code of server, and I'm having problem of running it properly. It looks like this:
/* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXCONNECTION 100 // maximum number of simultaneously connection allowed
const char CONTENTDIR[]="./contentdir" ; // this is the directory where keep all the files for requests
void error(const char *msg)
{
perror(msg);
exit(1);
}
void httpWorker(int *);// This function will handle request
char * fType(char *);
char * responseHeader(int, char *);// function that builds response header
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,MAXCONNECTION);
while(1){
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) error("ERROR on accept");
httpWorker(&newsockfd);//worker to fulfillthe request
}
close(sockfd);
return 0;
}
void httpWorker(int *sockfd){//sockfd contains all the information
int newsockfd = *sockfd;// create a local variable for sockfd
char buffer[256];// we will read the data in this buffer
char *token;// local variable to split the request to get the filename
bzero(buffer,256);// intialize the buffer data to zero
char fileName[50];
char homedir[50];
char * type;
strcpy(homedir,CONTENTDIR);// directory where files are stored.
char *respHeader; //response header
// start reading the message from incoming conenction
if (read(newsockfd,buffer,255) < 0) error("ERROR reading from socket");
//get the requested file part of the request
token = strtok(buffer, " ");// split string into token seperated by " "
token = strtok(NULL, " ");// in this go we read the file name that needs to be sent
strcpy(fileName,token);
// get the complete filename
if(strcmp(fileName,"/")==0) // if filename is not provided then we will send index.html
strcpy(fileName,strcat(homedir,"/index.html"));
else
strcpy(fileName,strcat(homedir,fileName));
type = fType(fileName);// get file type
//open file and ready to send
FILE *fp;
int file_exist=1;
fp=fopen(fileName, "r");
if (fp==NULL) file_exist=0;
respHeader = responseHeader(file_exist,type);
if ((send(newsockfd, respHeader,strlen(respHeader), 0) == -1) || (send(newsockfd,"\r\n", strlen("\r\n"), 0) == -1))
perror("Failed to send bytes to client");
free(respHeader);// free the allocated memory (note: the memory is allocated in responseheader function)
if (file_exist){
char filechar[1];
while((filechar[0]=fgetc(fp))!=EOF){
if(send(newsockfd,filechar,sizeof(char),0) == -1) perror("Failed to send bytes to client");
}
}
else{
if (send(newsockfd,"<html> <HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY>Not Found</BODY></html> \r\n", 100, 0) == -1)
perror("Failed to send bytes to client");
}
close(newsockfd);
}
// function below find the file type of the file requested
char * fType(char * fileName){
char * type;
char * filetype = strrchr(fileName,'.');// This returns a pointer to the first occurrence of some character in the string
if((strcmp(filetype,".htm"))==0 || (strcmp(filetype,".html"))==0)
type="text/html";
else if((strcmp(filetype,".jpg"))==0)
type="image/jpeg";
else if(strcmp(filetype,".gif")==0)
type="image/gif";
else if(strcmp(filetype,".txt")==0)
type="text/plain";
else
type="application/octet-stream";
return type;
}
//buildresponseheader
char * responseHeader(int filestatus, char * type){
char statuscontent[256] = "HTTP/1.0";
if(filestatus==1){
strcat(statuscontent," 200 OK\r\n");
strcat(statuscontent,"Content-Type: ");
strcat(statuscontent,type);
strcat(statuscontent,"\r\n");
}
else {
strcat(statuscontent,"404 Not Found\r\n");
//send a blank line to indicate the end of the header lines
strcat(statuscontent,"Content-Type: ");
strcat(statuscontent,"NONE\r\n");
}
char * returnheader =malloc(strlen(statuscontent)+1);
strcpy(returnheader,statuscontent);
return returnheader;
}
What I've done was to build a sub directory called contentdir under the folder where I put this server.c file, then I tried to get connetion from my browser ( I guess it doesn't matter with the browser). I just typically ran it by ./server 8080 and put localhost:8080/(and whatever the name of files in contentdir). It shows me black screen on the browser instead of the file contents. It stops connecting after reached MAXCONNECTION attmepts, I guess. I have tried html and jpg files, both no work.
Can someone help me to figure it out please?

Unexpected output in IPC using sockets

I am trying to write a server that can handle at most 5 concurrent clients.
Whenever a client gets successfully connected to the server & the number of clients is less than or equal to 5, the server sends a welcome message, generates a 5 digit unique random number for identifying that client, sends this number to the client and prints this number in the console.If the number of clients tends to be greater than 5, then for each new request, it just sends a message "Connection Limit Exceeded" to the client & closes the connection.
Client just prints the messages sent by the server.
The problem I'm facing is that, the random number is not being propagated properly to the client.Few times the client prints the same number as generated by the server but few times the client just prints 0(as the variable storing incoming value of that random number is initialized to 0).
What could be the reason behind this?
Here are the codes for client and server:
server:
/* A simple server in the internet domain using TCP
The port number is passed as an argument
This version runs forever, forking off a separate
process for each connection
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <stdlib.h>
void dostuff(int); /* function prototype */
void write_once (int sock);
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, pid, count = 0;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
pid = fork();
count++;
if (pid < 0)
error("ERROR on fork");
if (pid == 0 && count <=5 ) {
close(sockfd);
dostuff(newsockfd);
exit(0);
}
if (pid == 0 && count >= 5 ) {
close(sockfd);
write_once(newsockfd);
exit(0);
}
else close(newsockfd);
} /* end of while */
close(sockfd);
return 0; /* we never get here */
}
/******** DOSTUFF() *********************
There is a separate instance of this function
for each connection. It handles all communication
once a connnection has been established.
*****************************************/
void dostuff (int sock)
{
int n;
char buffer[256];
bzero(buffer,256);
n = write(sock,"Welcome\n",8);
if (n < 0) error("ERROR writing to socket");
srand((unsigned int)time(NULL));
int r = rand() % 90000 + 10000;
int converted_r = htonl(r);
n = write(sock, &converted_r, sizeof(converted_r));
if (n < 0) error("ERROR writing to socket");
printf("%d\n", r);
}
void write_once (int sock)
{
int n;
char buffer[256];
bzero(buffer,256);
n = write(sock,"Connection Limit Exceeded!!",28);
if (n < 0) error("ERROR writing to socket");
}
client:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
int received_int = 0;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
n = read(sockfd, &received_int, sizeof(received_int));
if (n < 0)
error("ERROR reading from socket");
printf("%d\n", ntohl(received_int));
close(sockfd);
return 0;
}
Reference
The issue is that TCP is a stream oriented protocol, and not packet oriented. So it may happen that
The first read() of the client reads what the first write() of the server sent ("Welcome")
The second read() of the client reads what the second write() of the server sent (Your number)
This is what you expect and what sometimes happens.
However, it might also be that the client reads the data of both writes of the server at once! This usually happens when
either the server aggregated the two writes to a single tcp-packet
or the client reads the data after both tcp segments with data arrived
You cannot make sure what happens and cannot rely on any specific behaviour.
How to fix this depends solely on your protocol. If the first message is always "Welcome\n", then try to read only 8 bytes first. If you happen to read n < 8 bytes, you have to retry and read 8-n bytes to get the rest of the message. Subsequently read sizeof(received_int) bytes, also watching for the real number of bytes received.
If the message is of variable length you will have to use some kind of framing like a preceding length-byte or something like that.

recvmsg() blocking on the first call but not on subsequent calls

I am having a problem with my client-server C program.
There are 2 main parts to the program. In part one, a message is sent from a Kernel module to a Userspace listener via multicast. I have tried and tested this part of the program many times and it works as expected.
The second part of the program transfers the message received to another machine (Machine 2). So, the Userspace listener from Machine 1 gets the message from the Kernel and then acts as a Client, opening up a socket between Machine 1 and Machine 2. The Server in Machine 2 receives the message and prints it to stdout. It then waits for another message.
The issue seems to be that the recvmsg() function blocks the first time but not in subsequent iterations. It is as if the information in the socket stays there and therefore prevents recvmsg() from blocking. The result is that the same piece of information is transferred in a loop.
I could be wrong on this recvmsg() problem. Perhaps someone has more experience and can point me in the right direction. The code and output are below. Many thanks.
NetLinkUser_Client.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#define MAX_PAYLOAD 1024
#define MY_GROUP 1
#define OUT_PORT "5001"
#define OUT_IP "192.168.xxx.xxx"
void client(char *msg) {
int socketfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
portno = atoi(OUT_PORT);
/* Create a socket point */
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd < 0) {
perror("ERROR opening socket");
exit(1);
}
server = gethostbyname(OUT_IP);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
/* Now connect to the server */
if (connect(socketfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
exit(1);
}
/* Now send a message to the server, this message
* will be read by server
*/
char buffer[MAX_PAYLOAD];
bzero(buffer,MAX_PAYLOAD);
strcpy(buffer, msg);
printf("\nThe contents of the buffer is: %s\n", buffer);
printf("\nThe length of the buffer is: %d\n", strlen(buffer));
/* Send message to the server */
n = write(socketfd, buffer, strlen(buffer));
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
bzero(buffer,MAX_PAYLOAD);
printf("%s\n",buffer);
close(socketfd);
}
int main(void)
{
int sock_fd;
struct sockaddr_nl user_sockaddr;
struct nlmsghdr *nl_msghdr;
struct msghdr msghdr;
struct iovec iov;
char* kernel_msg;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
if(sock_fd<0)
{
printf("Error creating socket because: %s\n", strerror(errno));
return -1;
}
memset(&user_sockaddr, 0, sizeof(user_sockaddr));
user_sockaddr.nl_family = AF_NETLINK;
user_sockaddr.nl_pid = getpid();
user_sockaddr.nl_groups = MY_GROUP;
bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));
while (1) {
ssize_t recvmsg_err = 0;
nl_msghdr = (struct nlmsghdr*) malloc(NLMSG_SPACE(1024));
memset(nl_msghdr, 0, NLMSG_SPACE(1024));
// The issue is not related to zeroing out msghdr
printf("The struct member msghdr.msg_namelen has the value : %d before zeroing out\n", msghdr.msg_namelen);
memset(&msghdr, 0, sizeof(msghdr));
printf("The struct member msghdr.msg_namelen has the value : %d after zeroing out\n", msghdr.msg_namelen);
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) nl_msghdr;
iov.iov_len = NLMSG_SPACE(1024);
msghdr.msg_name = (void*) &user_sockaddr;
msghdr.msg_namelen = sizeof(user_sockaddr);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
printf("The struct member msghdr.msg_namelen has the value : %d after initialization\n", msghdr.msg_namelen);
printf("Waiting to receive message\n");
// Execution not pausing here WHY??
printf("recvmsg() should wait here\n");
recvmsg_err = recvmsg(sock_fd, &msghdr, 0);
if(recvmsg_err == -1)
{
perror("Error in recvmsg\n");
}
printf("The value returned by recvmsg() is %d\n", recvmsg_err);
kernel_msg = (char*)NLMSG_DATA(nl_msghdr);
printf("Kernel message: %s\n", kernel_msg); // print to android logs
client(kernel_msg);
}
close(sock_fd);
}
MsgServer.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#define MAX_PAYLOAD 1024
void doprocessing (int sock) {
int n;
char buffer[MAX_PAYLOAD];
bzero(buffer,MAX_PAYLOAD);
n = read(sock,buffer,MAX_PAYLOAD);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("The length of the buffer is %d\n", strlen(buffer));
printf("Here is the message: %s\n",buffer);
}
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno, clilen;
char buffer[1024];
struct sockaddr_in serv_addr, cli_addr;
int n, pid;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
/* Now start listening for the clients, here
* process will go in sleep mode and will wait
* for the incoming connection
*/
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
printf("Waiting for initial connection in doProcessing() while loop\n");
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* Create child process */
pid = fork();
if (pid < 0) {
perror("ERROR on fork");
exit(1);
}
if (pid == 0) {
/* This is the client process */
close(sockfd);
doprocessing(newsockfd);
exit(0);
}
else {
close(newsockfd);
}
} /* end of while */
}
Client sample output
The struct member msghdr.msg_namelen has the value : 12 before zeroing out
The struct member msghdr.msg_namelen has the value : 0 after zeroing out
The struct member msghdr.msg_namelen has the value : 12 after initialization
Waiting to receive message
recvmsg() should wait here
The value returned by recvmsg() is 72
Kernel message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
The contents of the buffer is: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
The length of the buffer is: 54
The struct member msghdr.msg_namelen has the value : 12 before zeroing out
The struct member msghdr.msg_namelen has the value : 0 after zeroing out
The struct member msghdr.msg_namelen has the value : 12 after initialization
Waiting to receive message
recvmsg() should wait here
The value returned by recvmsg() is 72
Kernel message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
The contents of the buffer is: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
The length of the buffer is: 54
Server Sample Output
Waiting for initial connection in doProcessing() while loop
The length of the buffer is 54
Here is the message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
Waiting for initial connection in doProcessing() while loop
The length of the buffer is 54
Here is the message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
Waiting for initial connection in doProcessing() while loop
The length of the buffer is 54
Here is the message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
Waiting for initial connection in doProcessing() while loop
The length of the buffer is 54
Here is the message: Progger:7,root,1537,1533,1261,4s,192.168.xxx.xxx,5001
I understand that this might be hard to reproduce considering Netlink is involved. Thanks in advance for your help and sorry for the long post.
Edit: I have changed the code to check for errors in recvmsg() as was suggested but the output is exactly the same.

C sockets: forward a request to port 80 and read response

I have the following code (I'm working from code at http://www.linuxhowtos.org/C_C++/socket.htm) which I'm trying to turn into a proxy server:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void dostuff(int); /* function prototype */
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
//setup proxy:
int sockfd, newsockfd, portno, pid;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) {
fprintf(stderr,"***ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("***ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("***ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("***ERROR on accept");
pid = fork();
if (pid < 0)
error("***ERROR on fork");
if (pid == 0) {
close(sockfd);
dostuff(newsockfd);
exit(0);
}
else close(newsockfd);
} /* end of while */
close(sockfd);
return 0; /* we never get here */
}
/******** DOSTUFF() *********************
There is a separate instance of this function
for each connection. It handles all communication
once a connnection has been established.
*****************************************/
void dostuff (int sock)
{
int n;
char buffer[256];
bzero(buffer,256);
n = read(sock,buffer,255);
if (n < 0){
error("***ERROR reading from socket");
}
//printf("Here is the message: %s\n",buffer);
/*
***Forward message to port 80 and read response here
*/
n = write(sock,"I got your message",18);
if (n < 0) error("***ERROR writing to socket");
}
In the function "dostuff" I want to write 'buffer' to port 80, read the response and write this response back over port 20000 (argv[1]).
At the moment, when I set my browser's proxy to 172.16.1.218:20000, all I get is "I got your message". I want to change this to the response from the webpage!
Any pointers in the right direction greatly appreciated.
Here's what I've tried sofar (replace multi-line comment "Forward message to port 80 and read response here" with this code):
int sockfdi, portnoi, ni;
struct sockaddr_in serv_addri;
struct hostent *serveri;
portnoi =80;
sockfdi = socket(AF_INET, SOCK_STREAM, 0);
if (sockfdi < 0){
error("***ERROR opening socket");
}
serveri = gethostbyname("172.16.1.218");
if (serveri == NULL){
fprintf(stderr,"***ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addri, sizeof(serv_addri));
serv_addri.sin_family = AF_INET;
bcopy((char *)serveri->h_addr, (char *)&serv_addri.sin_addr.s_addr, serveri->h_length);
serv_addri.sin_port = htons(portnoi);
if (connect(sockfdi,(struct sockaddr *) &serv_addri,sizeof(serv_addri)) < 0){
error("***ERROR connecting");
}
printf("Please enter the message: ");
bzero(buffer,256);
But every time I try to connect via my webbrowser, the server echos: "***ERROR connecting: Connection refused"
Many thanks in advance,
This is a non-trivial task you set out to do. Currently, you're missing three things, an easy one and two difficult ones:
You have to open a network connection to the server you want to forward the call to (rather easy, see socket() and connect()).
You'll then have a duplex connection, that is two concurrent streams of data, one going from the client to the forwarded server and one from the forwarded server to the client. In order to cope with this concurrency, you either need two threads with blocking I/O or some sort of non-blocking I/O (see select() or AIO).
If you forward an HTTP request without changes to another server, you'll likely end up with invalid server names and IP addresses in the request. The request will then be rejected. So you'll need to parse the HTTP header, do some replacements and forward the modified HTTP request.

How to know if the client has terminated in sockets

Suppose, I have a connected socket after writing this code..
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
How can I know at the server side that client has exited.
My whole program actually does the following..
Accepts a connection from client
Starts a new thread that reads messages from that particular client and then broadcast this message to all the connected clients.
If you want to see the whole code... In this whole code. I am also struggling with one more problem that whenever I kill a client with Ctrl+C, my server terminates abruptly.. It would be nice if anyone could suggest what the problem is..
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
/*CONSTANTS*/
#define DEFAULT_PORT 10000
#define LISTEN_QUEUE_LIMIT 6
#define TOTAL_CLIENTS 10
#define CHAR_BUFFER 256
/*GLOBAL VARIABLE*/
int current_client = 0;
int connected_clients[TOTAL_CLIENTS];
extern int errno;
void *client_handler(void * socket_d);
int main(int argc, char *argv[])
{
struct sockaddr_in server_addr;/* structure to hold server's address*/
int socket_d; /* listening socket descriptor */
int port; /* protocol port number */
int option_value; /* needed for setsockopt */
pthread_t tid[TOTAL_CLIENTS];
port = (argc > 1)?atoi(argv[1]):DEFAULT_PORT;
/* Socket Server address structure */
memset((char *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; /* set family to Internet */
server_addr.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
server_addr.sin_port = htons((u_short)port); /* Set port */
/* Create socket */
if ( (socket_d = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "socket creation failed\n");
exit(1);
}
/* Make listening socket's port reusable */
if (setsockopt(socket_d, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value,
sizeof(option_value)) < 0) {
fprintf(stderr, "setsockopt failure\n");
exit(1);
}
/* Bind a local address to the socket */
if (bind(socket_d, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
fprintf(stderr, "bind failed\n");
exit(1);
}
/* Specify size of request queue */
if (listen(socket_d, LISTEN_QUEUE_LIMIT) < 0) {
fprintf(stderr, "listen failed\n");
exit(1);
}
memset(connected_clients,0,sizeof(int)*TOTAL_CLIENTS);
for (;;)
{
struct sockaddr_in client_addr; /* structure to hold client's address*/
int alen = sizeof(client_addr); /* length of address */
int sd; /* connected socket descriptor */
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
else printf("\n I got a connection from (%s , %d)\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
if (pthread_create(&tid[current_client],NULL,(void *)client_handler,(void *)sd) != 0)
{
perror("pthread_create error");
continue;
}
connected_clients[current_client]=sd;
current_client++; /*Incrementing Client number*/
}
return 0;
}
void *client_handler(void *connected_socket)
{
int sd;
sd = (int)connected_socket;
for ( ; ; )
{
ssize_t n;
char buffer[CHAR_BUFFER];
for ( ; ; )
{
if (n = read(sd, buffer, sizeof(char)*CHAR_BUFFER) == -1)
{
perror("Error reading from client");
pthread_exit(1);
}
int i=0;
for (i=0;i<current_client;i++)
{
if (write(connected_clients[i],buffer,sizeof(char)*CHAR_BUFFER) == -1)
perror("Error sending messages to a client while multicasting");
}
}
}
}
My client side is this (Maye be irrelevant while answering my question)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
void error(char *msg)
{
perror(msg);
exit(0);
}
void *listen_for_message(void * fd)
{
int sockfd = (int)fd;
int n;
char buffer[256];
bzero(buffer,256);
printf("YOUR MESSAGE: ");
fflush(stdout);
while (1)
{
n = read(sockfd,buffer,256);
if (n < 0)
error("ERROR reading from socket");
if (n == 0) pthread_exit(1);
printf("\nMESSAGE BROADCAST: %sYOUR MESSAGE: ",buffer);
fflush(stdout);
}
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
pthread_t read_message;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
bzero(buffer,256);
if (pthread_create(&read_message,NULL,(void *)listen_for_message,(void *)sockfd) !=0 )
{
perror("error creating thread");
}
while (1)
{
fgets(buffer,255,stdin);
n = write(sockfd,buffer,256);
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
}
return 0;
}
After accepting the connection, your recv() on the socket will return 0 or -1 in special cases.
Excerpt from recv(3) man page:
Upon successful completion, recv()
shall return the length of the message
in bytes. If no messages are available
to be received and the peer has
performed an orderly shutdown, recv()
shall return 0. Otherwise, -1 shall be
returned and errno set to indicate the
error.
So, if your client exited gracefully, you will get 0 from recv() at some point. If the connection was somehow lost, you may also get -1 and checking for appropriate errno would tell you if the connection was lost of some other error occured. See more details at recv(3) man page.
Edit:
I see that you are using read(). Still, the same rules as with recv() apply.
Your server can also fail when trying to write() to your clients. If your client disconnects write() will return -1 and the errno would probably be set to EPIPE. Also, SIGPIPE signal will be send to you process and kill him if you do not block/ignore this signal. And you don't as I see and this is why your server terminates when client presses Ctrl-C. Ctrl-C terminates client, therefore closes client socket and makes your server's write() fail.
See mark4o's answer for nice detailed explanation of what else might go wrong.
If the client program exits, then the OS on the client will close its end of the socket. When you call recv() it will return 0, or -1 with errno ECONNRESET if a TCP RST has been received (e.g. because you attempted to send data after the client had closed). If the whole client machine goes down, or the network becomes disconnected, then in that case you may not receive anything if the server is not trying to send anything; if that is important to detect, you can either send some data periodically, or set the SO_KEEPALIVE socket option using setsockopt() to force it to send a packet with no data after long periods (hours) of inactivity. When no acknowledgment is received, recv() will then return -1 with errno ETIMEDOUT or another error if more specific information is available.
In addition, if you attempt to send data on a socket that has been disconnected, by default the SIGPIPE signal will terminate your program. This can be avoided by setting the SIGPIPE signal action to SIG_IGN (ignore), or by using send() with the MSG_NOSIGNAL flag on systems that support it (Linux).

Resources