How to properly terminate a pthread? - c

I have a tcp echo server that creates a pthread for each client that connects to it. For each connection, I have a variable nbOfClients that increments.
When a client closes its connection, I detect it and decrease the number of clients. However the server keeps thinking that the client it alive and keeps on trying to read/write from the socket. I guessed that it was because of the thread that created the client and I tries to kill the thread with pthread_cancel all to non avail.
I want to kill the pthread associated to a certain client that closes its connection.
How can I go about it?
Here's my code :
static int nbOfClients = 0;
static pthread_t tid;
int main (int argc, char *argv[]) {
int bytes_to_read, arg, listen_sd, new_conn, sockfd, client_len, port;
struct sockaddr_in server, client_addr;
char *bp, buf[BUFLEN];
ssize_t n;
sockfd = 0;
switch(argc) {
case 1:
port = SERVER_TCP_PORT; // Use the default port
break;
case 2:
port = atoi(argv[1]); // Get user specified port
break;
default:
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(1);
}
// Create a stream socket
if ((listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
error("Cannot Create Socket!");
// set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c
arg = 1;
if (setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1)
error("setsockopt");
// Bind an address to the socket
bzero((char *)&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY); // Accept connections from any client
if (bind(listen_sd, (struct sockaddr *)&server, sizeof(server)) == -1)
error("bind error");
listen(listen_sd, MAX_CONNECTIONS); ///put a define constant indicating the maximum number of clients #define NB_CLIENTS 3
while (TRUE) {
client_len = sizeof(client_addr);
if ((new_conn = accept(listen_sd, (struct sockaddr *) &client_addr, (socklen_t *)&client_len)) == -1)
error("accept error");
if(new_conn > 0) {
if(nbOfClients < MAX_CONNECTIONS) {
printf("just here\n");
printf(">> Initializing remote address: %s\n", inet_ntoa(client_addr.sin_addr));
nbOfClients++;
fclose(fp);
printf("Connections to date: %u \n",nbOfClients);
printf("make thread\n");
pthread_create(&tid,NULL,&echo, (void *)new_conn);
printf("had thread\n");
}
else {
printf("connection limit reached\n");
if(send(new_conn, "Server full!\n", 13, 0) == -1)
perror("send");
close(new_conn);
}
}
}
return(0);
}
void * echo(void *arg) {
char buf[BUFSIZE]; /* message buffer */
int n, i = 0;
bzero(buf, BUFSIZE);
if(send((int)arg, "Welcome!!\n", 20, 0) == -1)
perror("send");
detect_closed_connection(arg);
while(TRUE) {
n = read((int)arg, buf, BUFSIZE);
/**read: read input string from the client*/
if(n < 0) {
perror("error reading from socket");
}
printf("Server received from client, %d bytes: %s\n", n, buf);
/**write: echo the input string in UPPERCASE back to the client*/
int len = strlen(buf);
for(i = 0; buf[i]; i++)
buf[i] = toupper(buf[i]);
n = write((int)arg, buf, len);
if(n < 0) {
error("ERROR writing to socket");
}
}
}
void detect_closed_connection(void * listenSocket) {
struct pollfd pfd;
pfd.fd = (int)listenSocket;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = 0;
while(pfd.revents == 0) {
if(poll(&pfd, 1, 100) > 0) {
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
char buffer[32];
if (recv((int)listenSocket, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
// if recv returns zero, that means the connection has been closed:
nbOfClients--;
pthread_cancel(tid);
}
}
}
}
Thanks.

You should check read() for returning 0 in the thread servering the client, as read() returns 0 in case the peer (client here) closed the connection.
After this line
n = read((int)arg, buf, BUFSIZE);
add
if (0 == n)
{
fprintf(stderr, "The client closed the connection.\n");
break;
}
Just before the thread function leave you could add the statement to decrement the number of running threads.
Also be aware that nbOfClients is accessed concurently by all the "client"-threads as well as by the main thread, so accessing it shall be protected, for example by using a mutex.
There is another issues, as the call to strlen() on the buffer read expects the buffer to be 0-terminate, which does not necessarily needs ot be the case, even if you sent 0-terminated "strings". read() might very well return the "string" the client sent in more then one part. So loop around read() until the 0-terminator had been received.
Do not make the thread end itself by calling pthread_cancel(), use pthread_exit() instead.

Related

Bad Address when reading from sockets in C

i'm facing a problem that i m not able to debug (it seems), let me explain the situation. I have a server that do something and 10 clients to connect. To test the code i use the same machine to host and connect the server and clients. Now, it seems that sometimes i m able to connect a random number of clients, like 4 and during the fifth connection the client side, after a good connection with the server, when i try to read from socket it gives me Bad address.
Now this number seems pretty casual, infact this error on the client side is given sometimes during the third connection, sometimes during the sixth connection and rarely never when i try to connect 10 clients. anyway most of the time this error occurs during the fifth connection.
Here the codes of server and client
Client:
void contact_server(int sockfd, int buffer_size, int reading_pipe, int writing_pipe) {
float* buff = (float*)calloc(buffer_size,sizeof(float));
int ret = 0;
while(1){
ret = 0;
while(ret == 0){
if(sizeof(float)*buffer_size <= sizeof(float)*2048){
ret = read(sockfd, buff, sizeof(float)*buffer_size);
if(ret == -1){
printf("(from server) Error description is: %s\n",strerror(errno));
exit(0);
}
}
else{
long long unsigned int sum = 0;
ret = read(sockfd, buff, sizeof(float)*2048);
if(ret == -1){
printf("(from server) Error description is : %s\n",strerror(errno));
exit(0);
}
sum+=ret;
int count;
for(count = 2048; sum < sizeof(float)*buffer_size; count+=2048, sum+=ret){
if(buffer_size-count < 2048){
ret = read(sockfd, &buff[count], sizeof(float)*(buffer_size-count));
if(ret == -1){
printf("(from server) Error description is : %s\n",strerror(errno));
exit(0);
}
}
else{
ret = read(sockfd, &buff[count], sizeof(float)*2048);
if(ret == -1){
printf("(from server) Error description is : %s\n",strerror(errno));
exit(0);
}
}
}
}
}// waiting for server
ret = write(writing_pipe,buff, sizeof(float)*buffer_size);// writing to parent process
if(ret == -1){
printf("(to parent) Error description is : %s\n",strerror(errno));
exit(0);
}
ret = 0;
while(ret == 0){
ret = read(reading_pipe, buff, sizeof(float)*buffer_size);
if(ret == -1){
printf("(from parent) Error description is : %s\n",strerror(errno));
exit(0);
}
}// waiting for parent process
ret = write(sockfd,buff, sizeof(float)*buffer_size);// writing to server
if(ret == -1){
printf("(to server) Error description is : %s\n",strerror(errno));
exit(0);
}
}
free(buff);
}
/* See server.c for more specific details
*
*
* Inputs:
*
* # int port:= the port of the server
* # char* server_address:= the server address
* # int buffer_size:= the buffer size written by the server and parent process
* # int reading_pipe:= to read from parent
* # int writing_pipe:= to write to parent
* */
int run_client(int port, char* server_address, int buffer_size, int reading_pipe, int writing_pipe){
int sockfd, connfd;
struct sockaddr_in servaddr, cli;
int sockaddr_len = sizeof(struct sockaddr_in);
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
fprintf(stderr,"Error: socket creation failed...\n");
exit(1);
}
else
printf("Socket successfully created..\n");
bzero(&servaddr, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(server_address);
servaddr.sin_port = htons(port);
// connect the client socket to server socket
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
printf("connection with the server failed...\n");
exit(0);
}
else
printf("connected to the server..\n");
// function for chat
contact_server(sockfd,buffer_size,reading_pipe,writing_pipe);
// close the socket
close(sockfd);
return 0;
}
Server:
void* server_thread(void* _args) {
// depacking args
thread_args_server* args = (thread_args_server*) _args;
float* buff = (float*)calloc(args->buffer_size,sizeof(float));
int ret;
while(1){
ret = 0;
while(ret == 0){
ret = read(args->reading_pipe, buff, sizeof(float)*args->buffer_size);
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
}// waiting for parent process
ret = write(args->client_desc,buff, sizeof(float)*args->buffer_size);// writing to client
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
ret = 0;
while(ret == 0){
if(sizeof(float)*args->buffer_size <= sizeof(float)*2048){
ret = read(args->client_desc, buff, sizeof(float)*args->buffer_size);
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
}
else{
long long unsigned int sum = 0;
ret = read(args->client_desc, buff, sizeof(float)*2048);
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
sum+=ret;
long long unsigned int count;
for(count = 2048;sum < sizeof(float)*args->buffer_size; count+=2048, sum+=ret){
if(args->buffer_size-count < 2048){
ret = read(args->client_desc, buff, sizeof(float)*(args->buffer_size-count));
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
}
else{
ret = read(args->client_desc, buff, sizeof(float)*2048);
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
}
}
}
}// waiting for client
ret = write(args->writing_pipe,buff, sizeof(float)*args->buffer_size);// writing to parent process
if(ret == -1){
printf("Error description is : %s\n",strerror(errno));
exit(0);
}
}
free(buff);
return NULL;
}
/* This function creates a server on your current ip address on the port: port,
* accept a maximum number of connections = max_num_conn, and create each thread per connection.
* each thread read a float vector from a client and writes on the writing_pipes[i] this vector, then
* wait for a float vector from reading_pipe[i] and send this vector to the client.
* The ideal situation is: client -> compute some instances of the mini batch, send to server the partial derivatives
* the thread of the client on the server side read these partial derivatives, send back to the father process with writing pipes,
* the parent process sum up all these partial derivatives, update the model, send back to the thread the model updated, the thread
* send to the client and the client goes on and compute again the new partial derivatives, and so on...
*
* Inputs:
*
* # int port:= the server port
* # int max_num_conn:= the maximum number of connections accepted by the server
* # int* reading_pipes:= a pipe writing for float vector to the thread
* # int* writing_pipes:= where the thread write its vector
* # int buffer_size:= the buffer of the vector
*
* */
int run_server(int port, int max_num_conn, int* reading_pipes, int* writing_pipes, int buffer_size, char* ip){
int socket_desc, client_desc,sockaddr_len = sizeof(struct sockaddr_in),c = 1;
int ret;
struct sockaddr_in server_addr;
struct sockaddr_in* client_addr;
struct sockaddr_in* client_addr2;
bzero(&server_addr, sizeof(server_addr));
inet_pton(AF_INET, ip, &(&server_addr)->sin_addr);
// socket creation
socket_desc = socket(AF_INET,SOCK_STREAM,0);
if(socket_desc == -1){
fprintf(stderr,"Error: can't create socket\n");
exit(1);
}
// Which connection can accept
server_addr.sin_addr.s_addr = INADDR_ANY;
// Ip family
server_addr.sin_family = AF_INET;
// Port
server_addr.sin_port = htons(port);
// Handling crash case to reuse the descriptor
ret = setsockopt(socket_desc,SOL_SOCKET,SO_REUSEADDR,&c,sizeof(int));
if(ret == -1){
fprintf(stderr,"Error: setsockopt failed..\n");
exit(1);
}
// Binding newly created socket to given IP and verification
if ((bind(socket_desc, (struct sockaddr *)&server_addr, sizeof(server_addr))) != 0) {
printf("socket bind failed...\n");
exit(0);
}
else
printf("Socket successfully binded..\n");
// listen on tot number of connections
ret = listen(socket_desc,max_num_conn);
if(ret == -1){
fprintf(stderr,"Error, listen failed\n");
exit(1);
}
int i = 0;
while(1){
client_addr = calloc(1,sizeof(struct sockaddr_in));
client_desc = accept(socket_desc,(struct sockaddr*)&client_addr,(socklen_t*)&sockaddr_len);
if((client_desc == 1 && errno == EINTR)) continue;
thread_args_server* thread = (thread_args_server*)malloc(sizeof(thread_args_server));
thread->idx = i;
thread->client_desc = client_desc;
thread->client_addr = client_addr;
thread->reading_pipe = reading_pipes[i];
thread->writing_pipe = writing_pipes[i];
thread->buffer_size = buffer_size;
pthread_t t;
pthread_create(&t,NULL,server_thread,thread);
pthread_detach(t);
printf("connected client id: %d\n",i);
i++;
}
pthread_exit(NULL);
return 0;
}
The error is given by ret = read(sockfd, &buff[count], sizeof(float)*(buffer_size-count)); on client side:
Thank you for the help

Not receiving File transfer from TCP connection

I am writing a TCP client and server protocol for a school project. The client sends a "GET \r\n" message and the server has to transfer "+OK\r\n", size of the file and the file, in case it exists in the server directory. I'm blocked in the file transfer
I tried to solve it at small steps at a time. I set up the connection, sent the request from the client and received the "OK" message from the server.
Now I opened the file in the server and tried to send it 128 bytes at a time to the client. The reading of the file works and apparently also the sending of the buffers but the client is not receiving anything...
Here's my server.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* Buffer length */
#define TIMEOUT 15 /* TIMEOUT */
/* FUNCTION PROTOTYPES */
void service(int s);
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
int conn_request_skt; /* passive socket */
uint16_t lport_n, lport_h; /* port used by server (net/host ord.) */
int bklog = 2; /* listen backlog */
int s; /* connected socket */
fd_set cset; // waiting for connection
struct timeval tval; // timeout
size_t n;
socklen_t addrlen;
struct sockaddr_in saddr, caddr; /* server and client addresses */
prog_name = argv[0];
if (argc != 2) {
printf("Usage: %s <port number>\n", prog_name);
exit(1);
}
/* get server port number */
if (sscanf(argv[1], "%" SCNu16, &lport_h)!=1)
err_sys("Invalid port number");
lport_n = htons(lport_h);
/* create the socket */
printf("creating socket...\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done, socket number %u\n",s);
/* bind the socket to any local IP address */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address", &saddr);
Bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
/* listen */
printf ("Listening at socket %d with backlog = %d \n",s,bklog);
Listen(s, bklog);
printf("done.\n");
conn_request_skt = s;
/* main server loop */
for ( ; ; )
{
printf("waiting for connection...\n");
/* accept next connection */
FD_ZERO(&cset);
FD_SET(conn_request_skt, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
n = Select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if ( n > 0 ){
addrlen = sizeof(struct sockaddr_in);
s = Accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s);
/* serve the client on socket s */
service(s);
} else {
printf("No connection request after %d seconds\n",TIMEOUT);
}
}
}
void service(int s) {
char buf[BUFLEN]; /* reception buffer */
char filename[BUFLEN];
int n;
long filesize;
uint32_t fsize;
FILE *fp;
for ( ; ; )
{
n = recv(s, buf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received request from socket %03d :\n", s);
sscanf(buf, "GET %s\r\n", filename);
strcpy(buf, "+OK\r\n");
printf("%s",buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while sending +OK\n");
// open file
fp = fopen(filename, "r");
if( fp == NULL){
//TODO close connection
}
// calculating dim of file
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
rewind(fp); // go back at beginning of file
fsize = htonl(filesize); // size file in network byte order
// sending file size
if(writen(s, &fsize, 4) != 4)
printf("Write error while sending file size\n");
while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
printf("%s", buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while buf\n");
}
printf("%s", buf);
printf("I am here\n");
}
}
}
While here is my client.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* BUFFER LENGTH */
#define TIMEOUT 15 /* TIMEOUT*/
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
char request[BUFLEN]; /* request buffer */
char rbuf[BUFLEN]; /* reception buffer */
uint32_t taddr_n; /* server IP addr. (net/host ord) */
uint16_t tport_n, tport_h; /* server port number (net/host ord) */
int s, len;
int result;
struct sockaddr_in saddr; /* server address structure */
struct in_addr sIPaddr; /* server IP addr. structure */
fd_set cset; // variables for timeout
struct timeval tval;
size_t n;
prog_name = argv[0];
if(argc < 4)
err_sys("Wrong number of parameters!\n");
// read address from first argument
taddr_n = inet_addr(argv[1]);
if (taddr_n == INADDR_NONE)
err_sys("Invalid address");
// read port number from second argument
if (sscanf(argv[2], "%" SCNu16, &tport_h)!=1)
err_sys("Invalid port number");
tport_n = htons(tport_h);
/* create the socket */
printf("Creating socket\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done. Socket fd number: %d\n",s);
/* prepare address structure */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = tport_n;
saddr.sin_addr = sIPaddr;
printf("trying to connect to the server...\n");
/* connect */
showAddr("Connecting to target address", &saddr);
Connect(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
// loop to request files
for (int i = 3 ; i < argc ; i++ ){ // i = 4 because the first file is the fourth argument
// check if file name is too big
if(strlen(argv[i]) >= BUFLEN - 6)
err_sys("The file name is too big for the buffer request!\n");
// create the string of bytes for the request
strcpy(request, "GET ");
strcat(request, argv[i]);
strcat(request, "\r\n");
len = strlen(request);
if(writen(s, request, len) != len){
printf("Write error\n");
break;
}
printf("waiting for response...\n");
// receive file from server
n = recv(s, rbuf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received reply from server\n");
uint32_t fsize;
printf("%s",rbuf);
if(strcmp(rbuf, "+OK\r\n") == 0){
n = recv(s, &fsize, 4, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
// received file dimension
fsize = ntohl(fsize);
}
while(fsize > 0){
printf("I am here1n\n");
// receive file
n = recv(s, rbuf, BUFLEN-1, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("I am here");
fsize -= n;
}
}
}
}
}
printf("===========================================================\n");
close(s);
exit(0);
}
The recv in the client where I am supposed to receive the file just blocks without receiving anything. I don't understand what I am missing...
The issue here is a common one: You're not being careful with message boundaries.
In your client, you do a recv and check whether the number of bytes is greater than 0. But then you don't do more length checking. You next do a strcmp on a particular string you're expecting to receive (+OK\r\n). But you might have received 3 bytes (+OK) or you might have received 10: (+OK\r\nXXXXX) or more [aside: also, recv doesn't guarantee your byte string is null-terminated]. There is nothing stopping the kernel on the far side from batching the preamble plus subsequent bytes into a single TCP packet. Likewise, there is nothing preventing the local side from aggregating multiple TCP packets into a single buffer.
You must provide message boundaries. If you're expecting your next message to be 5 bytes, then you should receive exactly 5 bytes (and retry if you get fewer -- being careful to check for EOF too in case the other side aborted early). Or, alternatively stick a buffering layer in front of your receive logic so that it will receive up to some large amount, return to you the number of bytes you want, and then save whatever is in excess for a subsequent "receive" call.
To restate this in a different way: Your server sends +OK\r\n, then it sends a four-byte length, then it starts sending the file. But that means your first recv on the client side could be receiving the preamble, plus the length, plus the first N bytes of the file all in one system call.
TCP does not respect, provide or enforce message boundaries.

Asynchronous C client for a multiclient C server

I have a client which is working fine, but whenever I run a new client, sometimes I don't receive the sent message on the other client already running, while using telnet it works flawlessly, the message "broadcasts" to all connected clients, and I want whenever a message is received to one of the clients to show even if I didn't already send a message.
Should I use select on clients ? and what should be changed ?
client.c:
#include <stdio.h> //printf
#include <string.h> //strlen
#include <sys/socket.h> //socket
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
int main(int argc , char *argv[]){
int sock;
struct sockaddr_in server;
char message[256] , server_reply[256];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 9034 );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){
perror("connect failed. Error");
return 1;
}
puts("Connected\n");
//keep communicating with server
for(;;){
printf("Enter message: ");
memset(message, 0, 256);
fgets(message, 256,stdin);
// scanf("%s" , message);
//Send some data
if( send(sock , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("Server Reply: %s\n", server_reply);
server_reply[0]='\0';
}
close(sock);
return 0;
}
server.c:
/*
** selectserver.c -- a cheezy multiperson chat server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void){
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
memset(buf, 0, 256);
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
The reason a client can't receive a message until they send one is because.
fgets(message, 256,stdin);
Will keep "reading" (and will therefore block) until an EOF or a newline character has been read from the input stream
Also, note that
if( recv(sock , server_reply , 256 , 0) < 0)
blocks if there is nothing to read, which will prevent that user from sending more messages to the server until there is something new to read from the server. Assuming that you've played online games before, I hope that you can see that such a setup would be rather annoying!
So, we have to find someway of checking to see if we can read from STDIN and the server socket without incurring a block. Using select() will prevent us blocking on the sever socket, but it wouldn't work for STDIN whilst using fgets() to read input from the user. This is because, as mentioned above, fgets() blocks until an EOF or newline is detected.
The main solution I have in mind is to replace fgets with a method buffer_message() that will only read from STDIN when it won't block on read (we'll use select() to implement this). We'll then place what is read into a buffer. If there is a full message, this message will then be written to the server. Otherwise, we'll let the control keep going through the program until there is something to read or write.
This is code from a recent university assignment I did and so a small portion of the code isn't mine
Declarations:
//directives are above (e.g. #include ...)
//message buffer related delcartions/macros
int buffer_message(char * message);
int find_network_newline(char * message, int inbuf);
#define COMPLETE 0
#define BUF_SIZE 256
static int inbuf; // how many bytes are currently in the buffer?
static int room; // how much room left in buffer?
static char *after; // pointer to position after the received characters
//main starts below
Main:
//insert the code below into main, after you've connected to the server
puts("Connected\n");
//set up variables for select()
fd_set all_set, r_set;
int maxfd = sock + 1;
FD_ZERO(&all_set);
FD_SET(STDIN_FILENO, &all_set); FD_SET(sock, &all_set);
r_set = all_set;
struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0;
//set the initial position of after
after = message;
puts("Enter message: ");
//keep communicating with server
for(;;){
r_set = all_set;
//check to see if we can read from STDIN or sock
select(maxfd, &r_set, NULL, NULL, &tv);
if(FD_ISSET(STDIN_FILENO, &r_set)){
if(buffer_message(message) == COMPLETE){
//Send some data
if(send(sock, message, strlen(message) + 1, 0) < 0)//NOTE: we have to do strlen(message) + 1 because we MUST include '\0'
{
puts("Send failed");
return 1;
}
puts("Enter message:");
}
}
if(FD_ISSET(sock, &r_set)){
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("\nServer Reply: %s\n", server_reply);
server_reply[0]='\0';
}
}
close(sock);
return 0;
//end of main
Buffer functions:
int buffer_message(char * message){
int bytes_read = read(STDIN_FILENO, after, 256 - inbuf);
short flag = -1; // indicates if returned_data has been set
inbuf += bytes_read;
int where; // location of network newline
// Step 1: call findeol, store result in where
where = find_network_newline(message, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 2: place a null terminator at the end of the string
char * null_c = {'\0'};
memcpy(message + where, &null_c, 1);
// Step 3: update inbuf and remove the full line from the clients's buffer
memmove(message, message + where + 1, inbuf - (where + 1));
inbuf -= (where+1);
flag = 0;
}
// Step 4: update room and after, in preparation for the next read
room = sizeof(message) - inbuf;
after = message + inbuf;
return flag;
}
int find_network_newline(char * message, int bytes_inbuf){
int i;
for(i = 0; i<inbuf; i++){
if( *(message + i) == '\n')
return i;
}
return -1;
}
P.S.
if( send(sock , message , strlen(message) , 0) < 0)
The above can also block if there's no space to write to the server, but there's no need to worry about that here. Also, I'd like to point out a few things you should implement for your client and your server:
Whenever you send data over a network, the standard newline is \r\n, or carriage return / newline, or simply the network newline. All messages sent between the client and the server should have this appended at the end.
You should be buffering all data sent between the server and the client. Why? Because you're not guaranteed to receive all packets in a message in a single read of a socket. I don't have time to find a source, but when using TCP/IP, packets for a message/file don't have to arrive together, meaning that if you do read, you may not be reading all of the data you intend to read. I'm not well versed in this, so please investigate this more. Open to having this edited / corrected

Why won't my server implementation run concurrently properly?

I am attempting to create a client/server system that can handle multiple concurrent connections using the unix system call fork.
The client enters a movie title, and the server will check if the movie was there or not. If it was there, it would tell the client the ranking, the name, and the box records.
looking at my forking implementation, the client asks for user input, however the program just simply goes pass it.
OUTPUT EXAMPLE:
connection made with client 127.0.0.1
PID IS 27270
--> all messages read - connection being closed
CLIENT: Please input an string: PID IS 0
At this line, CLIENT: Please input an string: PID IS 0, the user was suppose to input a string, however the program glances over it. How do I make the program take in the string from the client?
SERVER CODE:
int main()
{
int sock, clientsock, mlen, addrsize, msgct, chc, chct, pid;
struct sockaddr_in addr; //ipv4 address
char ch, buf[80];
/*
* Create a socket.
*/
sock = socket(AF_INET, SOCK_STREAM,0); //create socket (AF_NET shows its ipv4 internet connection, SOCK_STREAM shows its a tcp)
if (sock == -1)
{
perror("opening socket");
exit(-1);
}
//Bind socket to local address
/*
* Bind a name to the socket. Since the server will bind with
* any client, the machine address is zero or INADDR_ANY. The port
* has to be the same as the client uses.
*/
addr.sin_family = AF_INET;
addr.sin_port = htons (32351); //port number for local address
addr.sin_addr.s_addr = htonl (INADDR_ANY); //ip address (you can also hard code it)
if (bind(sock, (struct sockaddr *) &addr, //binding, first parameter : is the socket you created, &addr is the
sizeof (struct sockaddr_in)) == -1) //error checking
{
perror ("on bind");
exit (-1);
} //(at this moment we have binded socket)
/*
* Make the socket available for potential clients.
*/
//if there is connection or not?
if (listen(sock,1) == -1)
{
perror("on listen");
exit(-1);
}
//-------Text File Implementation-----------
FILE *fp;
char data[5][200];
char rank[5][2];
char name[5][255];
char value[5][100];
/* opening file for reading */
fp = fopen("movie.txt", "r");
if(fp == NULL) {
perror("Error opening file");
return(-1);
}
fgets (data[0], 200, fp);
int i = 1;
while(fgets (data[i], 200, fp)!=NULL)
{
/* writing content to stdout */
sscanf(data[i],"%s %[^$] %s",rank[i],name[i],value[i]);
puts(data[i]);
i+=1;
}
//CODE DOES NOT IMPLEMENT AFTER THIS WHILE LOOP
//close the file
fclose(fp);
addrsize = sizeof(struct sockaddr_in);
//THIS WHILE LOOP IS NOT BEING IMPLEMENTED...
while(1)
{
clientsock = accept(sock, (struct sockaddr *) &addr, &addrsize);
if (clientsock == -1)//error checking
{
perror("on accept");
exit(-1);
}
printf("connection made with client ");
printf ("%s\n", inet_ntoa (addr.sin_addr)); //also print client address
/* Create child process */
pid = fork();
if (pid < 0)
{
perror("ERROR on fork");
exit(1);
}
if (pid == 0)
{
/* This is the client process */
close(sock);
bool exist = false;
mlen = recv (clientsock, buf, 80, 0);
if (mlen < 0)
{
perror("ERROR reading from socket");
exit(1);
}
int lenS;
int which;
for(int i = 1; i<5; i++)
{
printf("%s\n\n", name[i]);
char *pch = strstr(name[i],buf);
if(pch != NULL)
{
which = i;
exist = true;
puts("GOOD");
}
else
{
puts("bad");
}
}
if(exist)
{
//SEND TO CLIENT FROM HERE!
printf("%s\n", rank[which]);
printf("%s\n", name[which]);
printf("%s\n", value[which]);
lenS = strlen(name[which]);
send (clientsock, name[which], lenS+1, 0);
}
else
{
//SEND TO CLIENT FROM HERE!!!!
printf("NOT HERE ");
send (clientsock, "NOT HERE", 9, 0);
}
printf("Here is the message: %s\n",buf);
exit(0);
}
else
{
close(clientsock);
printf(" --> all messages read - connection being closed\n");
}
}
}
CLIENT CODE:
int main()
{
int sock, addrsize;
struct sockaddr_in addr;
unsigned int in_address;
char buf[80];
int mlen;
/*
* Open a socket for Internet stream services.
*/
sock = socket(AF_INET, SOCK_STREAM,0); //creating a socket to connect to server, AF_INET : ipv4 internet connection, SOCK_STREAM tcp
if (sock == -1)
{ perror("opening socket");
exit(-1);
}
addr.sin_family = AF_INET;
addr.sin_port = htons (32351); //port number has to be the same as the one from server
in_address = 127 << 24 | 0 << 16 | 0 << 8 | 1; //ip address, local host, since we are running client and server on the same computer, it needs to have the same ip address
addr.sin_addr.s_addr = htonl (in_address);
if (connect (sock, (struct sockaddr *) &addr, //binding
sizeof (struct sockaddr_in)) == -1)
{
perror("on connect");
exit(-1);
}
char word[100];
int len;
printf("CLIENT: Please input an string: ");
scanf("%s", word);
//printf("You entered: %s\n", word);
len = strlen(word);
send (sock, word, len+1, 0);
mlen = recv (sock, buf, 80, 0);
printf ("%s\n\n\n\n\n\n\n", buf);
/*
* Do a shutdown to gracefully terminate by saying - "no more data"
* and then close the socket -- the shutdown is optional in a one way
* communication that is going to terminate, but a good habit to get
* into.
*/
if (shutdown(sock, 1) == -1)
{
perror("on shutdown");
exit(-1);
}
printf ("Client is done\n");
close(sock);
}
You are running the client and server programs on the same machine, with the same controlling terminal. The server master process, its client-service subprocess(es), and the independent client process therefore may all write to that terminal. They run independently and concurrently, so their outputs can be mashed up.
The fact that the PID IS 0 message is emitted after the prompt does not indicate that the client program has skipped accepting input, which indeed, I don't see how it could do. The prompt and the PID message come from different processes.
It would make things clearer to launch the server process and the client process from separate (virtual) terminals, so that their output is not mixed.

recv: Connection reset by peer

when I close my client connected to the server I get this error from the server and server shuts itself down. I know that client can terminate the connection gracefully but I am planning to send this out to some people and do not want my server to be shut just because they did not terminate gracefully. So what could actually prevent the server to be closed?
I am using sys/socket.h
Here's a part of my code
int server() {
//Set up variables
int sockfd, new_fd; //Listen on sock_fd, new connection on new_fd
struct sockaddr_in my_addr; //My(server) address information
struct sockaddr_in their_addr; //Connector's address information
socklen_t sin_size;
//Generate the socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
//Generate the end point
my_addr.sin_family = AF_INET; //Host byte order
my_addr.sin_port = htons(MYPORT); //Short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; //Auto-fill with my IP
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
== -1) {
perror("bind");
exit(1);
}
//Start listnening
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
while(TERMINATE == 0) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
//Create a new connection for the accepted socket
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
&sin_size)) == -1) {
perror("accept");
continue;
}
//some semaphore stuff
}
return 0;
}
int main(int argc, char *argv[]){
//extra stuff
//Set up mutex locks
pthread_mutex_init(&mutex, NULL);
sem_init(&empty, 0, 30);
sem_init(&full, 0, 0);
//Set up and run Threads
pthread_t threads[30]; //Array of threads
pthread_t server_thread;
pthread_attr_t attr; //Set of thread attributes
//Get the default thread attributes
pthread_attr_init(&attr);
signal(SIGINT, termination);//Wait for a SIGINT
//Loop to create threads and execute consumer_thread
for (int i = 0; i < 30; i++) {
//Set up data in structure
threadArray[i].threadID = i;
threadArray[i].running = 0;
threadArray[i].line_counter_pointer = &line_counter;
threadArray[i].vid_details_pointer = &vid_details;
pthread_create(&threads[i],&attr,consumer_thread, &threadArray[i]);
}
//Execute the producer_thread
pthread_create(&server_thread,&attr,producer_thread, NULL);
//Wait for all the threads to exit
for (int i = 0; i < 30; i++) {
pthread_join(threads[i],NULL);
}
//Destroy semaphores so that it can TERMINATE gracefully
sem_destroy(&empty);
sem_destroy(&full);
return 0;
}
void *producer_thread(void *param) {
server();//Runs the server() function
return NULL;
}
void *consumer_thread(void *param) {
//Pass variable
struct thread_params *threadStruct;
threadStruct = (struct thread_params *) param;
int *line_counter = threadStruct->line_counter_pointer;
vid_details_struct *vid_details = threadStruct->vid_details_pointer;
//End of pass
char found_result [MAXDATASIZE];
int queue_item = 0;
int numbytes;
struct timeval item_wait_time;// Get the current time
while (TERMINATE == 0) { //Main accept() loop
int new_fd;
//Use a variable that would be set to 0 after the client termination
//so that the current connection will be closed on both thread and
//client, that would make thread to go back to idle
int current_connection = 1;
//Acquire full semaphore
sem_wait(&full);
//Acquire mutex lock to protect buffer
pthread_mutex_lock(&mutex);
//some extra stuff including socket information
//now handling queue[queue_item]
new_fd = queue[queue_item].new_fd;
queue[queue_item].waiting = 0;
//Release mutex lock and empty semaphore
pthread_mutex_unlock(&mutex);
sem_post(&empty);
while (current_connection == 1) {
char buf[MAXDATASIZE];
//Receive the query
if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';//Set the end point of the string
if (!strcmp(buf,"q")) {//Client prompts to TERMINATE
current_connection = 0;//Flag the connection as closed
}
if (current_connection == 1) {//If still connected
//do something
if (send(new_fd, found_result, MAXDATASIZE, 0) == -1) {
perror("send");
close(new_fd);
exit(0);
}
}
}
close(new_fd); // Close the socket connection
//Wait for half a second before accepting a new request
usleep(500000);
}//End of the main while loop
FINISHEDSEMS++;
printf("Thread %d is closing\n", threadStruct->threadID);
return NULL;
}
This if-statement is what you need to look at:
if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
It's the only place you posted that has recv, so that's the error.
Take a look at the man page: recv returns the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from. If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno is set
So instead of having a call to exit (which terminates the process), try handling the error gracefully:
if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) < 0) {
// user disconnected or timeout (if you set a timeout)
// NO call to exit; use "continue" or "return", or something else
// to gracefully handle the break;
my_error_function("client disconnected\n");
break;
}
'Connection reset by peer' has a number of causes, but the most common one is that you have written to a connection that has already been closed by the peer. In other words, an application protocol error.

Resources