C : "same file descriptors of all client connections" (client server programming) - c

On every new client connection forked server process
different processes(other children of server i.e. exec) couldn't identify that client with same fd in used in forked child.
How can I differentiate client on other processes ??
accept call is returning same value if file descriptor new sockfd
/* server process */
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define SIZE sizeof(struct sockaddr_in)
void catcher(int sig);
int newsockfd;
int main(void)
{
int sockfd;
char c;
struct sockaddr_in server = {AF_INET, 7000, INADDR_ANY};
static struct sigaction act;
act.sa_handler = catcher;
sigfillset(&(act.sa_mask));
sigaction(SIGPIPE, &act, NULL);
/* set up the transport end point */
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket call failed");
exit(1);
}
/* bind an address to the end point */
if ( bind(sockfd, (struct sockaddr *)&server, SIZE) == -1)
{
perror("bind call failed");
exit(1);
}
/* start listening for incoming connections */
if ( listen(sockfd, 5) == -1 )
{
perror("listen call failed");
exit(1) ;
}
for (;;)
{
/* accept a connection */
if ( (newsockfd = accept(sockfd, NULL, NULL)) == -1)
{
perror("accept call failed");
continue;
}
printf("New connection. File descriptor fd is %d\n",newsockfd);
/* spawn a child to deal with the connection */
if ( fork() == 0)
{
while (recv(newsockfd, &c, 1, 0) > 0)
{
c = toupper(c);
send(newsockfd, &c,1, 0);
}
/* when client is no longer sending information the socket can be closed and the child process terminated */
close(newsockfd);
exit (0);
}
/* parent doesn't need the newsockfd */
close(newsockfd);
}
}
void catcher(int sig)
{
close(newsockfd);
exit (0);
}
/* client process */
#include <ctype.h>
#include <sys/types.h>
#include<stdlib.h>
#include<stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SIZE sizeof(struct sockaddr_in)
int main(void)
{
int sockfd;
char c, rc;
struct sockaddr_in server = {AF_INET, 7000};
/* convert and store the server's IP address */
server.sin_addr.s_addr = inet_addr("127.0.0.1");
/* set up the transport end point */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket call failed");
exit(1);
}
/* connect the socket to the server's address */
if ( connect (sockfd, (struct sockaddr *)&server, SIZE) == -1)
{
perror("connect call failed");
exit(1);
}
/* send and receive information with the server */
for(rc = '\n';;)
{
if (rc == '\n')
printf("Input a lower case character\n");
c = getchar();
send(sockfd, &c, 1, 0);
if (recv(sockfd, &rc, 1, 0) >0)
printf("%c", rc);
else
{
printf("server has died\n");
close(sockfd);
exit(1);
}
}
}

The file descriptor number is unique only within the process it exists in, and as soon as it's closed, it can be reused (for example the next time you call accept). It is not a good choice of "connection identifier".
In your case where you have a new process for each connection, the most natural identifier for a connection would be the process id - but you've thrown it away by not saving the return value of fork. In the parent process, fork returns the pid of the child process it created. You want to save this and use it later. In particular you can use it to kill the child process or identify when the child process exits (wait-family functions will tell you which child exited).
Of course if your whole model is to use a separate process for each connection, I'm not sure why you need to identify connections at all in the parent process. If each connection is not a completely independent process, you might do a lot better using threads instead of processes.

Related

Simple echo program using sockets in C echoing incorrect message after the first run

I am trying to learn the basic of network communication using sockets in C. My client program takes in a message from the user, echoes it server side and back, and prints out the received message. When I fire both of them up for the first time, they both work exactly as expected. However, if I quit the client side and then fire it up again while keeping the server program running, my echoed messages become off by one.
I assumed it was because the last message is getting caught in the pipe or something, and after poking around, I saw that someone suggested to use shutdown() to flush out the pipe, but that doesn't seem to be working. I also tried to zero out the buffers wherever I thought they may be lingering, but that didn't seem to help, either.
server.c
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
listening();
//Once first socket has been connected, begin echoing process
int i = 0;
while (1)
{
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
i++;
}
close(serverSocket);
return 0;
}
client.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#define PORT 12403
#define LOCALHOST "127.0.0.1"
#define BUFFER_MAX 1024
int socketStatus = 0;
void sigpipeHandler()
{
perror("Connection to server terminated\n");
socketStatus = 0;
}
int main()
{
int mySocket;
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
int count = 0;
//Create socket
if ((mySocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Get IP address of required host machine
char* hostName = "<host name removed>";
int portNumber = PORT;
char* ipAddr = NULL;
struct hostent* host = NULL;
host = gethostbyname(hostName);
ipAddr = inet_ntoa(*((struct in_addr*) host->h_addr_list[0]));
//Initialize server information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(portNumber);
if (inet_aton(ipAddr, (struct in_addr *)&socketInfo.sin_addr.s_addr) == 0)
{
perror("Error assigning IP address");
exit(errno);
}
//Set up sigpipe handler
signal(SIGPIPE, sigpipeHandler);
//Connect to server
if (connect(mySocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error connecting");
exit(errno);
}
//Indicate that socket is OK
socketStatus = 1;
while(1)
{
if(!socketStatus) {shutdown(mySocket, SHUT_WR); break;}
printf("Please enter a command.\n");
char command[BUFFER_MAX];
bzero(command, BUFFER_MAX);
fgets(command, sizeof(command), stdin);
send(mySocket, command, BUFFER_MAX, 0);
//Get echoed message
bzero(buffer, BUFFER_MAX);
recv(mySocket, buffer, sizeof(buffer), 0);
printf("Echo [%d]:%s\n", ++count, buffer);
}
//Close socket
close(mySocket);
return 0;
}
I did some cleanup on your server code and this seems to work.
For my testing, the client code is unchanged. But, as others have suggested, you should check the error codes from send and recv. Also, note that if you ctrl-c the server, the client will hang in the fgets, so it won't detect the server abort until you hit return after the prompt. Not a big deal, but I thought I'd mention it.
I also added a fork so you can have multiple clients talking to the same server instance simultaneously.
I tested this with two clients [in two xterm windows] talking with the single server instance.
I moved your echo code into a new function docomm. A small difference from your code is that any error from either recv or send breaks out of the loop and closes the connection. All connections from new clients are guaranteed to start with a recv call.
In your code, you would not always break out of the loop, but close the connection and call listening again. This would happen for either send or recv. If it happened on the wrong one, this might be the source of the problem you were having because you could do a send before a recv to a new client initially.
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
int forkflg = 1;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
void
docomm(void)
{
char buffer[BUFFER_MAX];
//Once first socket has been connected, begin echoing process
int i = 0;
while (1) {
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
break;
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
break;
i++;
}
printf("close\n");
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
while (1) {
listening();
if (! forkflg) {
docomm();
continue;
}
pid_t pid = fork();
if (pid == 0) {
docomm();
exit(0);
}
while (waitpid(0,NULL,WNOHANG) > 0);
}
close(serverSocket);
return 0;
}
UPDATE:
Just from a glance: 1) Can I ask why you created a fork flag if you never change the value of it? Should it be changed somewhere?
I used forkflg so you can set it to zero (e.g. int forkflg = 0;) to run sequentially. Or, you could add some code and parse argv looking for an option (e.g. -f) to set/clear it [for testing/debug purposes]. For production code, you'd want forkflg to be set and could remove the flag and just do the fork case always [adjusting the code to match].
Just tracing through the program mentally, it seems like the forking section will never be executed. Correct me where I'm wrong: after initially setting the socket to listen, the while loop will enter, and listening() will be called. Execution will halt in listening() until a connection is accepted.
Yes, that's true.
Control will return to main, where docomm() gets called. Control stays in docomm() until the connection breaks, at which point it returns to main and continue gets called, skipping the fork stuff and starting the process over again. So does the fork stuff ever get executed?
What you're describing is the behavior if forkflg is zero.
The fork is called if forkflg is set. Note that, in that case, docomm is called in the child and not the parent (because fork returned 0). So, the parent will not be blocked while the child does the echoing.
Thus, the parent returns immediately and is free to do the waitpid loop to reap any old children and restart the main/outer loop.
The waitpid loop only happens when a new connection comes in, so several children may have already terminated and will stay in zombie state until the waitpid loop gets executed [which will reap any/multiple pending children].
A cleaner way to reap the children might be to set up a signal handler for SIGCHLD and have it do the waitpid loop. This would reap all spent children immediately, without having to wait for a new connection to roll in.
Or, with the signal handler, add the waitpid loop to listening [inside the current loop] because if a SIGCHLD signal comes in, accept will return immediately with errno set to EINTR

C server client Bad file descriptor

I have been figuring out why the below code give a bad descriptor for the whole day now. Below is the server code, most of it references to Beej's guide.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "CountryData.c"
//This function determine if address is IPv4 or IPv6 IP address
void *getAddr_Type(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) { //If IPv4
return &(((struct sockaddr_in*)sa)->sin_addr);
}
else //if IPv6
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so it is stored in a variable first:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
int main(void){
readData(); //Read from CountryData.c
int status, sockfd, client_sockfd;
int pid; //fork return value
char buffer[1000];
int bytecount;
struct addrinfo hints, *res, *serverInfo; //res points to linked list of "struct addrinfo"; serverInfo also points to linked list of "struct addrinfo" for use in for loop.
struct sockaddr_storage client_addr; //Address information of the client
struct sigaction sa;
socklen_t address_size; //Initialize size of address
char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used.
memset(&hints, 0, sizeof(hints)); //emptying the structure
//Pass in value into "addrinfo" struct
hints.ai_family = AF_INET; //Using IPv4
hints.ai_socktype = SOCK_STREAM; //Using TCP
hints.ai_flags = AI_PASSIVE; //AI_PASSIVE = Own IP address
status = getaddrinfo(NULL, "8888", &hints, &serverInfo); //Initialising status return value and also passing in values to getaddrinfo().
//IP address is set to null. This will be filled in automatically by AI_PASSIVE.
if (status != 0) {
fprintf(stderr, "Error: %s\n", gai_strerror(status));//gai_strerror to print human readable error
exit(1);
}
//Loop through all results and bind to the first
for (res = serverInfo; res != NULL; res = res->ai_next) {
//(1)Initializing socket
if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { //Show error message if initializing socket file descriptor fails
perror("Socket");
continue;
}
int optValue=1;
if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof(int))) == -1) {
perror("Socket options");
exit(1);
} //(2)setting socket options. "SO_REUSEADDR" to prevent "Address already in use" and to allow reuse of the port
if ((bind(sockfd, res->ai_addr, res->ai_addrlen)) == -1) { //(3) Binding to local address and port
close(sockfd);
perror("Bind");
continue;
}
break;
}
freeaddrinfo(serverInfo); //Freeing "serverInfo" linked list
//However, if linked list is still empty, print error.
if (res == NULL) {
fprintf(stderr, "Error! Server is unable to bind.\n");
exit(1);
}
//If unable to listen, print error.
if ((listen(sockfd, 8)) == -1) { //(4) Listen for client connections, maximum of 8 waiting in queue
perror("Listen");
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("Running server program 'server' ... \n\n\nCountry Directory Server Started! PID: %d\n", getpid());
for(;;) //infinite loop for server to wait for client requests
{
memset(buffer, 0, 1000);
address_size = sizeof(client_addr);
client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &address_size);
if (client_sockfd == -1) {
perror("Accept");
close(client_sockfd);
exit(1);
}
inet_ntop(client_addr.ss_family, getAddr_Type((struct sockaddr *)&client_addr), i, sizeof(i));//retrieving IP address. "inet_ntop" is used for IPv6 compatibility.
printf("-------------------------------------------------------\n");
printf("Connection received from: %s\n\n", i);
if ((pid = fork()) == -1){ //Starts forking
perror("Failed to fork");
close(sockfd);
}
else if (pid == 0){ //child process
close(sockfd);//Child doesn't need this socket
memset(buffer, 0, 1000); //clear the buffer
if ((bytecount = recv(client_sockfd, buffer, 1000, 0)) == -1){//Receiving Client's input
perror("Server unable to receive");
close(client_sockfd);
exit(0);
}
else if ((strcasecmp(buffer, "END")) == 0){ //Nested If-statement; If client sends "end"
close(client_sockfd);
exit(0);
break;
}
else if (bytecount == 0) { //If "recv" returns 0, client has closed the connection
printf("Client (%d) has closed the connection.\n", getpid());
close(client_sockfd);
exit(0);
break;
}else {
printf("%s", buffer);
printf("%d", client_sockfd);
}
}
close(client_sockfd);
} //end of infinite while loop
}//End of main function
It successfully read client's input and print it out on the screen for the first for(;;) loop. The after the 2nd iteration, it shows Bad file descriptor
Below is the output in the server terminal after typing Hi in the client.
Johnny$ server
Running server program 'server' ...
Country Directory Server Started! PID: 18386
-------------------------------------------------------
Connection received from: 127.0.0.1
Accept: Bad file descriptor
Hi4
The number 4 is printing out the return value of the child file descriptor. This means that the loop ran once, then return an error. My expected output is simply to keep listening for the client's input and the server should constantly spit out what the client typed.
I am new to this server thing and am really having a major headache right getting this to work. Any help will be greatly appreciated.
Below is the client's code, if you're interested.
#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>
#include <arpa/inet.h>
#include <errno.h>
void welcome()
{
printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf(" Welcome to the Country Info Directory Service! \n");
printf(" ---------------------------------------------- \n");
printf("Usage :\n\n");
printf("1) At the '>' prompt, type in the name of the country\n");
printf(" you wish to search\n\n");
printf("2) To end program, type in 'end'\n");
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
}
//This function determine if address is IPv4 or IPv6 IP address
void *getAddr_Type(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) { //If IPv4
return &(((struct sockaddr_in*)sa)->sin_addr);
}
else //if IPv6
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[]) {
int sockfd, numOfBytes;
int retrieveInfo;
char buf[100];
struct addrinfo hints, *res, *serverInfo;
char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used.
if (argc != 2){
printf("Please enter in this format:\n'client':server-host-name\nFor example, if your hostname is vmwubuntu, please type:\nclient vmwubuntu [Enter]\n\n");
exit(1);
}
welcome();
memset(&hints, 0, sizeof(hints)); //emptying the structure
//Pass in value into "addrinfo" struct
hints.ai_family = AF_INET; //Using IPv4
hints.ai_socktype = SOCK_STREAM; //Using TCP
retrieveInfo = getaddrinfo(argv[1], "8888", &hints, &serverInfo);
if(retrieveInfo != 0) {
printf("Fail to retrieve address!");
}
//Loop through all results and bind to the first
for (res = serverInfo; res != NULL; res = res->ai_next) {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //(1)Initializing socket
if (sockfd == -1) { //Show error message if initializing socket file descriptor fails
perror("Socket");
continue;
}
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) { //Retrieving values from "struct addrinfo" through "res" pointer
//close(sockfd);
perror("Connection");
continue;
}
break;
}
if (res == NULL) {
printf("Failed to connect to server\n");
exit(1);
}
char input[1000];
char receive[1000];
for(;;)
{
memset(input, '\0', 1000); //Initialize buffer size to store user input
printf("Enter Country > ");
fgets(input, 1000, stdin); //Take in user input with 1000 as the buffer size
input[strlen(input) - 1] = '\0'; //Stripping the null terminator away
if (strcasecmp(input, "END") == 0){ //If user enters "end"(case is ignored), close the file descriptor and exit
close(sockfd);
exit(0);
}
else {//SEND
if((numOfBytes = send(sockfd, input, strlen(input), 0)) == -1){ //start of nested if statement
perror("Unable to send");
exit(1);
}
else if (numOfBytes != strlen(input)){ //If string is not sent in full
perror("Send");
close(sockfd);
exit(1);
}else{//for testing purposes
printf("%d\n",strlen(input));//for testing purpose
printf("%d\n", numOfBytes); //for testing purpose
}//End of nested if statement
}
}//End of for infinite loop
} //End of main()
Your child process appears to be not exiting, and instead proceeding with the same code as the parent. Then, you try to call accept with the closed file descriptor.
This is why I always put the child code in its own function, and always call _exit() immediately following. Note that I use _exit() instead of exit() to ensure that no parent atexit handlers are executed.
Furthermore, it helps to include PIDs in your log messages. Try using something like this:
#define INFO(fmt, ...) fprintf(stderr, "[%d] %s" fmt, getpid(), __FUNCTION__, __VA_ARGS__)
...
INFO("x=%d\n", x);
The child process in the sever side will try to accept the same fd.
How about to add a infinite loop before
else if (pid == 0){ //child process
close(sockfd);//Child doesn't need this socket
memset(buffer, 0, 1000); //clear the buffer
for (;;) {
if ((bytecount = recv(client_sockfd, buffer, 1000, 0)) == -1){//Receiving Client's input
perror("Server unable to receive");
close(client_sockfd);
exit(0);
}
else if ((strcasecmp(buffer, "END")) == 0){ //Nested If-statement; If client sends "end"
close(client_sockfd);
exit(0);
break;
}
else if (bytecount == 0) { //If "recv" returns 0, client has closed the connection
printf("Client (%d) has closed the connection.\n", getpid());
close(client_sockfd);
exit(0);
break;
}else {
printf("%s", buffer);
printf("%d", client_sockfd);
}
}
}

Static variable in C

I have this program that is a part of a server with tcp protocol, that gets a number from the client and uses it in another function.
Also I have a static int type variable that i want to count each time the server gets a message from a client, but each time it does not keep its value;
The variable is counter
Can you guys tell me why this is happening?
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// used port
#define PORT 1114
// error code
extern int errno;
static int counter;
int main ()
{
struct sockaddr_in server; // structure used by server
struct sockaddr_in from;
char message[100]; //message received from client
int socketDescriptor; //socket descriptor
//creating socket
if ((socketDescriptor = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("[server]Error at socket\n");
return errno;
}
//preparing data structures
bzero (&server, sizeof (server));
bzero (&from, sizeof (from));
//filling structures
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl (INADDR_ANY);
server.sin_port = htons (PORT);
//attach socket to descriptor
if (bind (socketDescriptor, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
{
perror ("[server]Error at bind\n");
return errno;
}
//server is listening
if (listen (socketDescriptor, 2) == -1)
{
perror ("[server]Error at listen\n");
return errno;
}
/serving concurrent the clients
while (1)
{
int client;
int length = sizeof (from);
printf ("[server]Waiting at port %d...\n",PORT);
fflush (stdout);
//accepting client
client = accept (socketDescriptor, (struct sockaddr *) &from, &length);
counter ++;
switch(fork())
{
case -1:
perror("fork err\n");
exit(2);
case 0:
//error if failed connection
if (client < 0)
{
perror ("[server]Error at accept\n");
continue;
}
//conenction established
bzero (message, 100);
printf ("[server]Waiting for message...\n");
fflush (stdout);
//reading message
if (read (client, message, 100) <= 0)
{
perror ("[server]Error at read\n");
close (client); //closing connection
continue; //keep listening
}
printf ("[server]Message was received%s\n", message);
//this is where I want to increment counter, when I want to verify message
int number;
number = atoi(message);//convert char to int
printf("The number is: %d\n", number);//print number
printf("The counter is : %d\n", counter);
fflush(stdout);
exit(2);
}
close (client);
} /* while */
} /* main */
Just move counter++ to the parent process. When the child process starts it gets a copy of counter and the one you modify does not affect it's copy (original actually) in the parent process. If you update it in the parent process you will achieve what you want.
int main(int argc, char ** argv)
{
int number;
int listenfd, connfd, n;
pid_t childpid;
socklen_t clilen;
char message[MAXLINE], answer[MAXLINE];
struct sockaddr_in clientaddr, serveraddr;
int counter;
counter = 0;
// create socket
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("ERROR at creating socket\n");
exit(2);
}
// preparation of socket address
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(PORT);
// bind the socket
bind(listenfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr));
// listen to the socket
listen(listenfd, LIMIT);
printf("Server running, waiting for connections at port : %d\n", PORT);
while (1)
{
clilen = sizeof(clientaddr);
// accept a connection
connfd = accept(listenfd, (struct sockaddr *) &clientaddr, &clilen);
printf("Recieved guest\n");
switch (fork())
{
case -1:
perror("fork err\n");
exit(2);
case 0:
/* eroare la acceptarea conexiunii de la un client */
if (connfd < 0)
{
perror("[server]Eroare la accept().\n");
continue;
}
/* s-a realizat conexiunea, se astepta mesajul */
bzero(message, 100);
printf("[server]Asteptam mesajul...\n");
fflush(stdout);
/* citirea mesajului */
if (read(connfd, message, 100) <= 0)
{
perror("[server]Eroare la read() de la client.\n");
close(connfd); /* inchidem conexiunea cu clientul */
continue; /* continuam sa ascultam */
}
printf ("[server]Message was received...%s\n", message);
fflush(stdout);
number = atoi(message);
printf("The number is: %d\n", number);
printf ("%d\n", counter + 1);
_exit(0); // The child should not create further grand children
default:
counter++;
break;
}//switch
}//for
close(connfd);
}//main
You appear to be forking before setting the counter. Since forking duplicates the entire process, the copy of counter in each child process is different from the parent.
Sharing variables between applications in C can be tricky, but actually there's a pretty easy fix for your case and it will improve performance too. If you look at your code, you're forking after the connect and then handling, in the child, the possibiliy that the connfd < 0. If you handled that in the parent process, the counter could belong to the parent and could be incremented before the fork.
A few other notes here come to mind. Remember, fork duplicates the parent process, so the children in your case are still within a while(1) loop. When you continue you loop back to the next iteration of the while(1) loop, but this doesn't seem correct; you want the child process to exit when it's done handling the connection. This also means as you accept connections, you fork but the fork never dies - I guess that's more a "process leak" than a memory leak, but will certainly eat up memory. Finally, just to throw it out there, forking to handle each request is probably the slowest way to concurrently handle connections. I've had great success with pthread in this case. Since threads share a process space, the threads can even persist and handle many connections before dying ( put the connections on a queue and have the threads poll it, for example ) becasuse they can continue to share connections with their "parent" ( though it's really a sibling thread in this case).

Multiclient Server Implementation in C

I am trying to implement multi client server using C. I have write code for server and client is a software that will continuously send some packet to server. Server will read the packet and process it. I am trying for 5 connection at a time to server. But there is some problem with server code that I have written due to which I can not connect 5 client to server. Exact Problem is that when client is trying to connect with server it gets connection for but when I close client software and try to start again it is not getting connection. Following is my code for server side. can anybody help me regarding this problem.
#include <ctype.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/file.h>
int sock; /* The socket file descriptor for our "listening"socket */
int connectlist[15]; /* Array of connected sockets so we know who we are talking to */
fd_set socks; /* Socket file descriptors we want to wake up for, using select() */
int highsock = 1; /* Highest #'d file descriptor, needed for select() */
struct sockaddr_in client_address[5];
unsigned int clientLength = sizeof(client_address) ;
#define PORTNO (int)49153
int port; /* The port number after conversion from ascport */
struct sockaddr_in server_address; /* bind info structure */
int reuse_addr = 1;
struct timeval timeout; /* Timeout for select */
int readsocks; /* Number of sockets ready for reading */
int err = 0 ;
#define BACKLOG (int)10
void deal_with_data(int listnum /* Current item in connectlist for for loops */)
{
//Here I am trying to read packet from client s/w and process it
}
void setnonblocking(int sock)
{
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0)
{
printf("fcntl(F_GETFL)_error");
exit(0);
}
opts = (opts | O_NONBLOCK);
if (fcntl(sock,F_SETFL,opts) < 0)
{
printf("fcntl(F_SETFL)_error");
exit(0);
}
return;
}
void build_select_list()
{
int listnum; /* Current item in connectlist for for loops */
FD_ZERO(&socks);
FD_SET(sock,&socks);
for (listnum = 0; listnum < 5; listnum++)
{
if (connectlist[listnum] != 0)
{
FD_SET(connectlist[listnum],&socks);
if (connectlist[listnum] > highsock)
highsock = connectlist[listnum];
}
}
}
void handle_new_connection() {
int listnum; /* Current item in connectlist for for loops */
int connection; /* Socket file descriptor for incoming connections */
connection = accept(sock, (struct sockaddr *)&client_address[highsock], &clientLength);
if (connection < 0)
{
printf("accept_error");
exit(0);
}
setnonblocking(connection);
for (listnum = 0; (listnum < 5) && (connection != -1); listnum ++)
if (connectlist[listnum] == 0)
{
printf("\nConnection accepted: FD=%d; Slot=%d\n",
connection,listnum);
printf("Connection accepted from %s\n",inet_ntoa(client_address[highsock].sin_addr));
connectlist[listnum] = connection;
connection = -1;
}
if (connection != -1)
{
printf("\nNo room left for new client.\n");
write(connection,"Sorry, this server is too busy.Try again later!\r\n",80);
close(connection);
}
printf("return from handle_new_connection\n");
}
void read_socks(void)
{
int listnum; /* Current item in connectlist for for loops */
if (FD_ISSET(sock,&socks))
handle_new_connection();
for (listnum = 0; listnum < 5; listnum++)
{
if (FD_ISSET(connectlist[listnum],&socks))
{
//printf("read_socks2\n");
deal_with_data(listnum);
}
}
}
int main (/*int argc, char *argv[]*/)
{
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0)
{
printf("socket_error");
exit(EXIT_FAILURE);
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr));
setnonblocking(sock);
memset((char *) &server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &server_address,sizeof(server_address)) < 0 )
{
printf("bind_error");
close(sock);
exit(EXIT_FAILURE);
}
if((err = listen(sock,10)) == -1)
{
printf("listen_error");
}
highsock = sock;
memset((char *) &connectlist, 0, sizeof(connectlist));
while (1)
{ /* Main server loop - forever */
build_select_list();
timeout.tv_sec = 2;
timeout.tv_usec = 0;
readsocks = select(highsock+2, &socks, (fd_set *) 0,(fd_set *) 0, &timeout);
if (readsocks < 0)
{
printf("select_error");
exit(EXIT_FAILURE);
}
if (readsocks == 0)
{
printf(".");
fflush(stdout);
}
else
{
read_socks();
}
} /* while(1) */
} /* main */
Your problem is that your main loop doesn't exit when the socket has been closed by the client. This means that it cannot accept new connections. I would use fork() to do the processing of the data from the socket, and the main() function to accept connections and fork() the process. Also, you need to have some code which will kill the fork()ed (i.e. to check if the client has disconnected in the fork()ed process) process (since it won't close by itself and it takes up memory).
EDIT:
Ok, I can't find a call to recv() in your program. According the the recv(3) man page it will return 0 if the client disconnect "gracefully" and return -1 and set errno to ECONNRESET if the client forcefully disconnected. In order to use fork, I would (in the main() function) wrap your while loop in this:
int childpid = fork();
if(childpid == -1) {
printf("Could not fork process");
exit(EXIT_FAILURE);
}
else if(childpid == 0) { /* in child process*/
while(/* check if the socket has been closed */) {
/* While loop stuff */
}
/* free up memory */
exit(EXIT_SUCCESS);
}
And your main function should be in a loop, waiting for new connections.
N.B. I have not tested any of this code, so it might not work. But if you read the man pages for fork(3) and recv(3) it should work.
Aren't you better off using something off the shelf like Apache and customize it as necessary?

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