Multithreaded Server in LInux - c

I am Working on server Programming in C in Linux environment.At same time there might be several clients linked with the Server.It's my first project on server Programming and not having much experience in Multi-threading.Till Now i have Programmed following for Multithreading.
I want to get some idea about following Points.
1.How many clients will the server Handle?
2.will pthread_t thr;create multiple Threads or i need to do something like pthread_t thr[X] to create X Threads ?
3.How can i get the data for each client in the following code ?
4.Will pthread_create create a new copy of *connection_handler for each new client connected.
void dieWithError(char *errormsg);
FILE *file;
void *connection_handler(void *socket_desc)
{
//Get the socket descriptor
int sock = *(int*)socket_desc;
int read_size;
char *message , client_message[2000];
//Receive a message from client
while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
{
//end of string marker
client_message[read_size] = '\0';
printf("%s",client_message);
fprintf(file,"%s", client_message);
//clear the message buffer
memset(client_message, 0, 2000);
}
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
return 0;
}
int main(int argc, char** argv) {
int sock_desc = 0, connfd = 0,listenfd =0;
struct sockaddr_in serv_addr;
int clntSock;
struct sockaddr_in echoClntAddr;
unsigned int clntLen;
char sendBuff[1025];
char recvBuff[10025];
int n = 0;
pthread_t thr;
sock_desc = socket(AF_INET, SOCK_STREAM, 0);
if(sock_desc < 0 )
dieWithError("Unable to open Socket\n");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET ;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(7024);
if(bind(sock_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
dieWithError("bind failed\n");
if(listen(sock_desc,3) < 0)
dieWithError("listen failed\n");
file = fopen("testServer.txt", "w");
clntSock = sizeof(struct sockaddr);
int i =0;
while((connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr,(socklen_t*)&clntSock)))
{
puts("Connection accepted");
if( pthread_create( &thr, NULL , connection_handler , (void*) &connfd) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( thread_id , NULL);
puts("Handler assigned");
}
if (connfd < 0)
{
perror("accept failed");
return 1;
}
return (EXIT_SUCCESS);
}
void dieWithError(char *errormsg){
printf("%s", errormsg);
}

It depends on the server machine, and how many threads it can handle. Generally speaking, the more threads you have, the more time the operating system will spend just switching between threads meaning the threads themselves will have less time doing any real work.
E.g. pthread_t thr; is a single variable, able to hold a single thread. You can reuse it for multiple threads, but then you loose the other threads leading to a resource leak. You probably need an array or a list if you want multiple threads.
Structures. One structure per thread, each structure containing the thread information (pthread_t), the client socket, and everything else needed by both the main thread and the client-handling thread.
No. connection_handler is a function, there will only be one instance of a function per process in memory, functions are not copied.

Related

Linux C: segfault error 4 in libmysqlclient.so.18.0.0

I'm trying to implement a multithreaded tcp server with the following code.
int main(int argc , char *argv[])
{
int socket_desc , client_sock , c , *new_sock;
struct sockaddr_in server , client;
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
while( (client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c)) )
{
puts("Connection accepted");
pthread_t sniffer_thread;
new_sock = malloc(1);
*new_sock = client_sock;
if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) new_sock) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( sniffer_thread , NULL);
puts("Handler assigned");
}
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
return 0;
}
/*
* This will handle connection for each client
* */
void *connection_handler(void *socket_desc)
{
//Get the socket descriptor
int newsockfd = *(int*)socket_desc;
int read_size;
char *message , buffer[2000];
int n;
struct auth details;
char* reply;
char cmd[100] = {0};
//Receive a message from client
while( (read_size = recv(newsockfd , buffer , 2000 , 0)) > 0 )
{
printf("Incoming data: %s", buffer);
if(parseData(cmd, buffer, "/", "-")) /* If received data is compliant */
{
details = authenticate(cmd);
if (details.verified) // If said user can execute this command.
{
reply = execute(cmd); // Execute.
n = write(newsockfd, reply, strlen(reply));
if (n < 0) { error("ERROR writing to socket"); }
free(reply);
reply = NULL;
}
else // Authentication failed.
{
n = write(newsockfd, details.error_msg, strlen(details.error_msg));
if (n < 0) { error("ERROR writing to socket"); }
else{
if(debug) printf("sent: %s\n", details.error_msg);
}
}
}
}
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
//Free the socket pointer
free(socket_desc);
return 0;
}
The authenticate function is defined in an implementation file:
struct auth authenticate(const char* const command)
{
struct auth data;
char* db_pwd;
char* perm;
char* username = "testUser";
char query[200] = {0};
data.verified = FALSE;
data.error_msg = "";
printf("in auth\n");
MYSQL_ROW row;
snprintf(query, 200, "SELECT password FROM userlist WHERE user='%s'", username);
if ((row = mysql_fetch_row(run_query(query)))) // if user is on list
{
db_pwd = row[0];
if(!strcmp(db_pwd, password))
{
if (!(strcmp(command, "test")))
{
data.verified = TRUE;
}
}
}
return data;
}
I have a working single threaded version of this program(main) with the same implementation file, the segmentation fault occurs only with the multithreaded version.
Am I doing something wrong in my code?
How should I proceed with debugging?
Any ideas would be appreciated.
Read documentation of MySQL C API, notably ยง 23.8.12 C API Threaded Function Descriptions It internally uses a socket connection to the mysqld server process, so you have to serialize all MySQL functions.
So, define your global mutex, and protect all the functions doing mysql calls with that mutex (from a request to fetching all the rows of the reply). Better yet, design your application so that only one single thread (usually the main one) is using mysql functions.
Compile with all warnings and debug info (gcc -Wall -g). Use the debugger (gdb) and perhaps valgrind
If unfamiliar with mutexes, you might want to read a pthread tutorial

How to use fork() to call subroutines in parallel using C

I have the following code in which I'm running a server using a TCP port. I need to fork and place calls to a subroutine to invoke clients so that they can run in parallel and connect with the server.
As of now, I have just been able to make serial implementation in which calls are placed through a loop, I've just seem to have hit a road block, it will be great if someone can take the pain to go through the code and guide me.
The following is the main. The client subroutine just resides in client.c and seems to work fine. If need be I can paste that too.
main(int argc, char *argv[])
{
struct sockaddr_in manager, client;
struct hostent *cp;
int sockdescriptor, td;
int len;
char buf[BLEN];
int j;
int n;
int num_nodes;
pid_t pid;
key_t key;
int shmid;
int *port_num;
sockdescriptor = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
memset((char *) &manager, 0, sizeof(struct sockaddr_in));
manager.sin_family = AF_INET;
manager.sin_addr.s_addr = INADDR_ANY;
manager.sin_port = htons((u_short) 0); /* dynamically assigning port */
bind(sockdescriptor, (struct sockaddr *) &manager, sizeof(struct sockaddr_in));
len = sizeof(struct sockaddr_in);
listen(sockdescriptor, QUELEN);
/***************************** Getting port by getsockname() **********************************/
/* */
/* */
if(getsockname(sockdescriptor, (struct sockaddr *) &manager, &len) == -1){
perror("getsockname failed!");
return -1;
}
/* */
/* */
/**********************************************************************************************/
printf("manager port %d\n", (int) ntohs(manager.sin_port));
/********************************* Creating Shared Memory *************************************/
/* */
/* */
key = 1234;
if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0){
perror("shmget is broken!");
exit(1);
}
if ((port_num = shmat(shmid, NULL, 0)) == (int *) -1){
perror("shmat is broken!");
exit(1);
}
*port_num = (int) ntohs(manager.sin_port);
/* */
/* */
/**********************************************************************************************/
for(j = 0; j < num_nodes ; j++){
if((pid = fork()) == 0){ // child process
while(1) {
len = sizeof(struct sockaddr_in);
td = accept(sockdescriptor, (struct sockaddr *) &client, &len);
// close(sockdescriptor); //closing listening socket
cp = gethostbyaddr((char *) &client.sin_addr, sizeof(struct in_addr), AF_INET);
// printf("Connected from %s\n", cp->h_name);
client_num++;
printf("client %d port %d\n",client_num, *port_num);
sprintf(buf, "%d",nonce);
send(td, buf, strlen(buf), 0);
n = recv(td, buf, sizeof(buf), 0);
printf("client %d says %s\n",client_num, buf);
close(td); /* client request processed, close this client's socket */
close(sockdescriptor);
exit(0);
} // end of while loop
}
// else if((pid = fork()) > 0){
client_prog(); // Calls to this subroutine need to be via forked processes
// close(td);
// exit(0);
// } // else if ends here
} // end of the for loop
To have num_nodes clients, do:
for(j = 0; j<num_nodes; j++)
if (fork() == 0) {
close(sockdescriptor);
client_prog();
exit(0);
}
Then to have a separate process handling each client connection, such that multiple clients can proceed in parallel, I suggest that you replace the entire for loop with something like this:
while(1) {
td = accept(sockdescriptor, ...);
client_num++;
if (fork() == 0) {
close(sockdescriptor);
/* handle client interaction here */
send(...) / receive(...)
exit(0);
} else {
close(td);
}
}
Note that there is a race condition: If num_nodes is greater than SOMAXCONN, it is possible that connections are dropped. Spawning the server process first does not eliminate the race condition. This sort of thing is safer with pipes, that are pre-opened before fork.

UDP multi-client chat server

I have a multi-client chat server and for some reason only the first client is being added. I used a tutorial to help get me started. I have included my code below. When I try and add another client it doesnt appear to be added. If I add one client I get a response from the server like I want but only the first message I enter then after that it stops sending correctly.
Server Code:
int main(void)
{
struct sockaddr_in my_addr, cli_addr[10],cli_temp;
int sockfd;
socklen_t slen[10],slen_temp;
slen_temp = sizeof(cli_temp);
char buf[BUFLEN];
int clients = 0;
int client_port[10];
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
{
printf("test\n");
err("socket");
}else{
printf("Server : Socket() successful\n");
}
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr* ) &my_addr, sizeof(my_addr))==-1)
{
err("bind");
}else{
printf("Server : bind() successful\n");
}
int num_clients = 0;
while(1)
{
//receive
printf("Receiving...\n");
if (recvfrom(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&cli_temp, &slen_temp)==-1)
err("recvfrom()");
if (clients <= 10) {
cli_addr[clients] = cli_temp;
client_port[clients] = ntohs(cli_addr[clients].sin_port);
clients++;
printf("Client added\n");
//printf("%d",clients);
int i;
for(i=0;sizeof(clients);i++) {
sendto(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&cli_addr[i], sizeof(cli_addr[i]));
}
}
}
close(sockfd);
return 0;
}
I have included the client code as well in case it helps.
void err(char *s)
{
perror(s);
exit(1);
}
sig_atomic_t child_exit_status;
void clean_up_child_process (int signal_number)
{
/* Clean up the child process. */
int status;
wait (&status);
/* Store its exit status in a global variable. */
child_exit_status = status;
}
int main(int argc, char** argv)
{
struct sockaddr_in serv_addr;
int sockfd, slen=sizeof(serv_addr);
char buf[BUFLEN];
struct sigaction sigchld_action;
memset (&sigchld_action, 0, sizeof (sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction (SIGCHLD, &sigchld_action, NULL);
int pid,ppid;
if(argc != 2)
{
printf("Usage : %s <Server-IP>\n",argv[0]);
exit(0);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
err("socket");
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(argv[1], &serv_addr.sin_addr)==0)
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
pid = fork();
if (pid<0) {
err("Fork Error");
}else if (pid==0) {
//child process will receive from server
while (1) {
bzero(buf,BUFLEN);
//printf("Attempting to READ to socket %d: ",sockfd);
fflush(stdout);
//recvfrom here
if (recvfrom(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&serv_addr, &slen)==-1)
err("recvfrom()");
printf("The message from the server is: %s \n",buf);
if (strcmp(buf,"bye\n") == 0) {
ppid = getppid();
kill(ppid, SIGUSR2);
break;
}
}
}else {
//parent will send to server
while(1){
printf("Please enter the message to send: ");
bzero(buf,BUFLEN);
fgets(buf,BUFLEN,stdin);
printf("Attempting to write to socket %d: ",sockfd);
fflush(stdout);
//send to here
if (sendto(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&serv_addr, slen)==-1)
{
err("sendto()");
}
}
}
close(sockfd);
return 0;
}
Several problems jump out at me. First, every time you receive a message it will consider that to be a new client. Instead of just incrementing the clients variable for a message, you'll need to scan through the array to see if the source address is already present. Second, sizeof(clients) will return a static value (probably 4) depending on how many bytes an int occupies on your machine. That loop should be for( int i = 0; i < clients; i++ ).
You also have a variable named num_clients which is not used. Is that supposed to be there for something and maybe is causing some confusion?
Finally, instead of using the magic value 10 all over the place, use #define MAX_CONNECTIONS 10 and then replace all those numbers with MAX_CONNECTIONS. It's a lot easier to read and change later.

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.

full duplex communication between server & client

I want to make a code for full duplex communication between server & client using this code.
I got error in receive-message thread from server side & in send-message thread from client side.
please help me to solve this errors & suggest me if any other changes are required.
Thanks :)
server.cpp
int newsockfd, n;
void error(const char *msg)
{
perror(msg);
exit(1);
}
void* recvFn( void* data )
{
char buffer[256];
while(n==0){
memset( buffer, sizeof(buffer), 0);
n = recv(newsockfd,buffer,255,MSG_PEEK);
if(n>0){
printf("cliet: ");
printf("%s",buffer);
}
}
return NULL;
}
void* sendFn( void* data )
{
char temp[255], buffer[255];
while(n==0){
memset( buffer, sizeof(buffer), 0);
fgets(temp,255,stdin);
sprintf(buffer,"clent: %s",temp);
n = send(newsockfd,buffer,strlen(buffer),MSG_EOR);
}
return NULL;
}
int main(int argc, char *argv[])
{
int sockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
pthread_t recvThread, sendThread;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
memset( &serv_addr, sizeof(serv_addr), 0);
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
int on = 1;
if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof( on ) ) != 0 ) {
close( sockfd );
return -1;
}
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
n = 0;
int rc;
rc = pthread_create( &recvThread, NULL, recvFn, NULL);
if(rc){
printf("error in receive-message thread\n");
return -1;
}
rc = pthread_create( &sendThread, NULL, sendFn, NULL);
if(rc){
printf("error in send-message thread\n");
return -1;
}
close(newsockfd);
close(sockfd);
pthread_cancel(recvThread);
pthread_cancel(sendThread);
return 0;
}
client.cpp
int sockfd, n;
void* recvFn( void* data )
{
char buffer[255];
while( n==0 ){
memset( buffer, sizeof(buffer), 0);
n = recv(sockfd,buffer,255,MSG_PEEK);
if(n>0){
printf("server: ");
printf("%s",buffer);
}
}
return NULL;
}
void* sendFn( void* data )
{
char temp[255], buffer[255];
while( n==0 ){
memset( buffer, sizeof(buffer), 0);
fgets(temp,255,stdin);
sprintf(buffer,"clent: %s",temp);
n = send(sockfd,buffer,strlen(buffer),MSG_EOR);
}
return NULL;
}
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int portno;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
pthread_t recvThread, sendThread;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
portno = atoi(argv[2]);
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
memset( &serv_addr, sizeof(serv_addr), 0);
serv_addr.sin_family = AF_INET;
memcpy((char *)server->h_addr,(char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR on connecting");
n = 0;
int rc;
rc = pthread_create( &sendThread, NULL, sendFn, NULL);
if(rc){
printf("error in send-message thread\n");
return -1;
}
rc = pthread_create( &recvThread, NULL, recvFn, NULL);
if(rc){
printf("error in receive-message thread\n");
return -1;
}
close(sockfd);
pthread_cancel(recvThread);
pthread_cancel(sendThread);
return 0;
}
Your pthread_mutex operations are completely pointless. You're referring only to local variables inside the mutex lock other than n, which should be local in each thread and newsockfd which also should not be global, see below. (Don't you think that a function which calls recv should have a local variable to capture the number of bytes read, and not share that silly little temporary variable globally with other threads?)
Your main thread is in a while loop, creating threads like crazy. Also, inside that loop it has closed the one and only accepted socket, right after creating the threads.
You forgot to put your accept inside the loop, evidently.
Also you seem to think that the main loop will somehow wait for the pair of threads to terminate before launching new ones. You are missing pthread_join calls to wait for the threads finish communicating. If you want the thread to keep going while the main loop accepts new connections using new threads, you should make those threads detached with pthread_detached or using a thread-creation attribute which makes them detached. Non-detached threads which are not pthread_join-ed continue to occupy resources.
Speaking of shutdown, is it really the correct condition that the threads keep looping while n == 0? As soon as n is set to nonzero by one of the threads, the shutdown condition is met. But a nonzero value is normal: some bytes written or read. Your reader should terminate the loop when there is a fatal receive error on the socket, or the read returns zero.
Also, you are evaluating n == 0 outside of the mutex!
If you want to accept multiple concurrent connections, each with its pair of threads, then you cannot use a single global socket. You have to give each pair of threads their own socket. The two threads within each pair do not have to use a mutex to share the socket. The socket calls are thread-safe in the kernel and the threads are not both doing reads or writes; one is reading and one is writing.
Other problems
Your sender keeps sending uninitialized garbage: a buffer that was never set to contain any data.
You have a bzero of 256 bytes on an array of 255 bytes.
Also
Don't use bzero (or bcopy, etc). It's a BSD-ism from the 1980's. The C language was finally standardized in 1989 by ANSI and soon after in 1990 by ISO. At that time, it already had the library functions memset, memcpy and memmove.
I think 22 years later, it is safe to retire bcopy, dontcha think?

Resources