This articles covers a simple proxy server application for multiple clients in C. It is based on Linux based socket programming.
If you are comfortable with basics of socket programming and multithreading in c then this article is for you!
To revise basics of socket programming, please visit my earlier article. Link:
This proxy server application is as follows:
- Three programs will run at a time(in separate Linux terminals, of course..!)
- One is main server. It acts as a echo server.
- One is proxy server. It forwards data from client to main server. And server response back to client
- Last one is a client.
2. It supports multiple clients.
3. There will be only one main server and one proxy server
4. Proxy server will accept three arguments from command line. These include server IP address followed by server port address and then proxy's port address. Since proxy is running on a local machine. Its IP address will be 127.0.0.1 by default. No need to mention that in the command line.
5. Client will accept two arguments. These include proxy's IP address and port.
Now you can directly jump to the code. Copy-paste it and run it for further study.
For newbies, I'm explaining how programs work in brief. The application flow is as follows:
- Client will accept proxy details and connect to the specified proxy through socket
- Proxy server will accept server details and store the details in variables
- Proxy server also create a socket at the mentioned proxy port and start listening for incoming client connections continuously on that port.
- For each successfully accepted client connection, proxy server will create a thread and pass client details(client file descriptor) and server details(ip and port) to that newly created thread. Lets call this thread as client thread.
- This client thread will create a socket connection and connect to the main server. Now each thread will be responsible for communication between that client and main server.
- While main server will accept client connections and create its own thread for each client. Lets call these threads as server threads. This will help main server to differentiate traffic between multiple clients.
- Here, main trick is to manage multiple client connections via proxy server. I implemented it using multithreading.
- Instead of multithreading, you can use a 'select command' which can also serve the same purpose. It is used for synchronous IO multiplexing. Following link covers the details of it with multi chat client-server application using select command:
http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html
Why multi-threading?
Answer: Codes are modular in multithreading and all accepted client-server connections are independent of each other. When client exits, that particular thread will exit. But the disadvantage of threads is that threads create complexity in the program.
Answer: Codes are modular in multithreading and all accepted client-server connections are independent of each other. When client exits, that particular thread will exit. But the disadvantage of threads is that threads create complexity in the program.
So, Thats all folks!
Now you are ready to run your own proxy server application with the help of following code. See the results and modify it as per your requirements.
I wrote comments in the codes for clarity of the logic.
I wrote comments in the codes for clarity of the logic.
1. Copy the following code and save in a file called "mainserver.c"
#include <sys/socket.h>
#include <sys/types.h>
#include <resolv.h>
#include <string.h>
#include <pthread.h>
#include<unistd.h>
// A thread function
// A thread is created for each accepted client connection
void *runSocket(void *vargp)
{
int c_fd =(int)vargp; // get client fd from arguments passed to the thread
char buffer[65535];
int bytes = 0;
while(1)
{
//receive data from client
memset(&buffer,'\0',sizeof(buffer));
bytes = read(c_fd, buffer, sizeof(buffer));
if(bytes <0)
{
//perror("read");
}
else if(bytes == 0)
{
}
else
{
//send the same data back to client
// similar to echo server
write(c_fd, buffer, sizeof(buffer));
//printf("client fd is : %d\n",c_fd);
//printf("From client:\n");
fputs(buffer,stdout);
}
fflush(stdout);
};
return NULL;
}
// main entry point
int main()
{
int client_fd;
char buffer[100];
int fd = 0 ;
struct sockaddr_in server_sd;
// add this line only if server exits when client exits
signal(SIGPIPE,SIG_IGN);
// create a socket
fd = socket(AF_INET, SOCK_STREAM, 0);
printf("Server started\n");
memset(&server_sd, 0, sizeof(server_sd));
// set socket variables
server_sd.sin_family = AF_INET;
server_sd.sin_port = htons(5010);
server_sd.sin_addr.s_addr = INADDR_ANY;
// bind socket to the port
bind(fd, (struct sockaddr*)&server_sd,sizeof(server_sd));
// start listening at the given port for new connection requests
listen(fd, SOMAXCONN);
// continuously accept connections in while(1) loop
while(1)
{
// accept any incoming connection
client_fd = accept(fd, (struct sockaddr*)NULL ,NULL);
//printf("accepted client with id: %d",client_fd);
// if true then client request is accpted
if(client_fd > 0)
{
//multithreading variables
printf("proxy connected\n");
pthread_t tid;
// pass client fd as a thread parameter
pthread_create(&tid, NULL, runSocket, (void *)client_fd);
}
}
close(client_fd);
return 0;
}
2. Copy the following code and save it in a file called 'proxy.c'
#include <sys/socket.h>
#include <sys/types.h>
#include <resolv.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include<unistd.h>
#include<netdb.h> //hostent
#include<arpa/inet.h>
int hostname_to_ip(char * , char *);
// A structure to maintain client fd, and server ip address and port address
// client will establish connection to server using given IP and port
struct serverInfo
{
int client_fd;
char ip[100];
char port[100];
};
// A thread function
// A thread for each client request
void *runSocket(void *vargp)
{
struct serverInfo *info = (struct serverInfo *)vargp;
char buffer[65535];
int bytes =0;
printf("client:%d\n",info->client_fd);
fputs(info->ip,stdout);
fputs(info->port,stdout);
//code to connect to main server via this proxy server
int server_fd =0;
struct sockaddr_in server_sd;
// create a socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_fd < 0)
{
printf("server socket not created\n");
}
printf("server socket created\n");
memset(&server_sd, 0, sizeof(server_sd));
// set socket variables
server_sd.sin_family = AF_INET;
server_sd.sin_port = htons(atoi(info->port));
server_sd.sin_addr.s_addr = inet_addr(info->ip);
//connect to main server from this proxy server
if((connect(server_fd, (struct sockaddr *)&server_sd, sizeof(server_sd)))<0)
{
printf("server connection not established");
}
printf("server socket connected\n");
while(1)
{
//receive data from client
memset(&buffer, '\0', sizeof(buffer));
bytes = read(info->client_fd, buffer, sizeof(buffer));
if(bytes <= 0)
{
}
else
{
// send data to main server
write(server_fd, buffer, sizeof(buffer));
//printf("client fd is : %d\n",c_fd);
printf("From client :\n");
fputs(buffer,stdout);
fflush(stdout);
}
//recieve response from server
memset(&buffer, '\0', sizeof(buffer));
bytes = read(server_fd, buffer, sizeof(buffer));
if(bytes <= 0)
{
}
else
{
// send response back to client
write(info->client_fd, buffer, sizeof(buffer));
printf("From server :\n");
fputs(buffer,stdout);
}
};
return NULL;
}
// main entry point
int main(int argc,char *argv[])
{
pthread_t tid;
char port[100],ip[100];
char *hostname = argv[1];
char proxy_port[100];
// accept arguments from terminal
strcpy(ip,argv[1]); // server ip
strcpy(port,argv[2]); // server port
strcpy(proxy_port,argv[3]); // proxy port
//hostname_to_ip(hostname , ip);
printf("server IP : %s and port %s" , ip,port);
printf("proxy port is %s",proxy_port);
printf("\n");
//socket variables
int proxy_fd =0, client_fd=0;
struct sockaddr_in proxy_sd;
// add this line only if server exits when client exits
signal(SIGPIPE,SIG_IGN);
// create a socket
if((proxy_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\nFailed to create socket");
}
printf("Proxy created\n");
memset(&proxy_sd, 0, sizeof(proxy_sd));
// set socket variables
proxy_sd.sin_family = AF_INET;
proxy_sd.sin_port = htons(atoi(proxy_port));
proxy_sd.sin_addr.s_addr = INADDR_ANY;
// bind the socket
if((bind(proxy_fd, (struct sockaddr*)&proxy_sd,sizeof(proxy_sd))) < 0)
{
printf("Failed to bind a socket");
}
// start listening to the port for new connections
if((listen(proxy_fd, SOMAXCONN)) < 0)
{
printf("Failed to listen");
}
printf("waiting for connection..\n");
//accept all client connections continuously
while(1)
{
client_fd = accept(proxy_fd, (struct sockaddr*)NULL ,NULL);
printf("client no. %d connected\n",client_fd);
if(client_fd > 0)
{
//multithreading variables
struct serverInfo *item = malloc(sizeof(struct serverInfo));
item->client_fd = client_fd;
strcpy(item->ip,ip);
strcpy(item->port,port);
pthread_create(&tid, NULL, runSocket, (void *)item);
sleep(1);
}
}
return 0;
}
int hostname_to_ip(char * hostname , char* ip)
{
struct hostent *he;
struct in_addr **addr_list;
int i;
if ( (he = gethostbyname( hostname ) ) == NULL)
{
// get the host info
herror("gethostbyname");
return 1;
}
addr_list = (struct in_addr **) he->h_addr_list;
for(i = 0; addr_list[i] != NULL; i++)
{
//Return the first one;
strcpy(ip , inet_ntoa(*addr_list[i]) );
return 0;
}
return 1;
}
3. Copy the following code and save it in a file called 'client.c'
#include <sys/socket.h>
#include <sys/types.h>
#include <resolv.h>
#include <string.h>
// main entry point
int main(int argc, char* argv[])
{
//socket variables
char IP[200];
char port[200];
char buffer[65535];
int sd;
struct sockaddr_in client_sd;
printf("\nEnter proxy address:");
fgets(IP,sizeof("127.0.01\n")+1,stdin);
fputs(IP,stdout);
printf("\nEnter a port:");
fgets(port,sizeof("5000\n")+1,stdin);
fputs(port,stdout);
if((strcmp(IP,"127.0.0.1\n"))!=0 || (strcmp(port,"5000\n"))!=0)
{
printf("Invalida proxy settings. Try again...");
}
else
{
// create a socket
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("socket not created\n");
}
memset(&client_sd, 0, sizeof(client_sd));
// set socket variables
client_sd.sin_family = AF_INET;
client_sd.sin_port = htons(5000);
// assign any IP address to the client's socket
client_sd.sin_addr.s_addr = INADDR_ANY;
// connect to proxy server at mentioned port number
connect(sd, (struct sockaddr *)&client_sd, sizeof(client_sd));
//send and receive data contunuously
while(1)
{
printf("Type here:");
fgets(buffer, sizeof(buffer), stdin);
write(sd, buffer, sizeof(buffer));
printf("\nServer response:\n\n");
read(sd, buffer, sizeof(buffer));
fputs(buffer, stdout);
//printf("\n");
};
//close(sd);
}
return 0;
}
How to run the programs?
I'm posting snapshots of my program. Please follow steps in sequence:
1. Compile and run main server
2. Compile and run proxy server
3. Compile and run client
Please note that
- -lpthread is a command used to compile the code if multi-threading is used. So, commands to compile proxy.c and mainserver.c are little different from client.c
- And client.c file accepts 5000 as a port address for proxy server. It is hard coded in the file. you can either comment that line or write other valid port address if you need it that way.
Hey! This program is pretty helpful.
ReplyDeleteI had a doubt whether we can convert this to http proxy server?
Thanks for posting these source modules. I've found them to be exactly precisely what I was looking for; which was simply a C-coded example of cli-to-serv/serv-to-cli interactivity framework atop which (and after some degree of re-working) I can begin integrating a new private P2P network protocol which i'd already begun working on;... however I had missing the basic functionality of proxy emulation which will basically make my library about 3X more effective; that is, if I ever get it finished :) I appreciate your work and would like you to know that i'll most likely use these sources (however revised IDK yet) in part of my project. I hope that's OK. If not, let me know @ strawberry.lokote.13@gmail.com --- thankx again!
ReplyDeletehello if I want to download a file via proxy from mainserver.c how do I download a file from client what modifications will there be needed also can the code cache the server response at the proxy so that already downloaded file does not get again downloaded.
ReplyDeleteI have got segmentation fault(core dumped) error when run proxy.c code
ReplyDeleteKing Casino Login | All your games online and - Community Khabar
ReplyDeleteLogin King Casino, Play, and Win! Login King Casino, Play. Login King Casino, Play. Login febcasino King Casino, Play. Login King https://jancasino.com/review/merit-casino/ Casino, Play. Login King www.jtmhub.com Casino, Play. Login deccasino King communitykhabar Casino,