Thread: Concurrent chat, Sockets

  1. #1
    Registered User
    Join Date
    Sep 2008
    Posts
    30

    Concurrent chat, Sockets

    Hey guys,
    Im writing this client/server chat program. Its in C and runs on UNIX. Its multiproccessed using the fork() function. My problem is that I cant seem to get my child functions to communicate with each other. I have a global 2d array sockets[][] that holds the socket for each client. Here is my code. Its more of pseudo code because its really the concept thats getting me and not the coding of it. So don't look at it for syntax errors just at the concepts/execution. I can't seem to put my finger on whats happening exactly and I think that's where my mistake is.


    SERVER.c

    Code:
    #define BUFFSIZE 256
    //global 2d array to hold client sockets
    int sockets[BUFFSIZE][BUFFSIZE];
    main(){
    
    
    *code*
    //code above sets up the socket for listening. i.e bind/listen
    
    /*code below is a loop that constantly accepts new clients and forks() a new process for each new client. Also adding the new socket for each new client to the sockets[][] array.*/
    
    while(1){
        socket = accept();
    
       sockets[0][index] = socket;
       index++;
       pid = fork();
       if (pid == 0)
       dostuff(socket);
      
    }
    }
    
    
    dostuff(int socket){
    
    //recvs a msg from the client of this child process
    recv(socket, buff);
    
    /*for loop that iterates through the sockets[][] and sends the recved buff from the client of this child process to every client in the sockets[][] array*/
    for(i=0; i<BUFFSIZE;i++){
    
       int sd;
       //whenthere are no more sockets the for loop ends
       if((sd = socket[0][i]) == 0)
       break;
    
       else
        send(sd, buff);
    
    }
    
    }

    the client code is kinda trivial. It just connects and send/recvs in a loop.

    Any help is greatly appreciated.
    Last edited by jakemott; 11-29-2008 at 09:29 AM.

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You do realize that when you fork, each child gets it's own private copy of anything in the parent process, right? [1] So a child changing the content of any global variable in a child, it only changes it's own copy, not the parent's copy.

    [1] To be pedantic, the data is put in a "copy-on-write" state, so that when the child process is made, the OS don't have to copy EVERYTHING. Instead, the data is made "read-only", and when the OS sees the page-fault of writing to read-only data, the OS makes a copy. That way, not only does it save time, but also there aren't any copies of data that never changes from the initial fork.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Sep 2008
    Posts
    30
    Is it the same with pthreads? if So, what would be the best way to communicate between child processes? Do you have to write functions that take the child processes as arguments and update the variables in them or something?

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by jakemott View Post
    Is it the same with pthreads? if So, what would be the best way to communicate between child processes? Do you have to write functions that take the child processes as arguments and update the variables in them or something?
    No, for good and bad, threads share all the memory between each other (each thread has it's own stack memory, but there's absolutely nothing preventing a thread from reading data held in another threads stack - assuming it knows where the other thread's stack is)-

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    Sep 2008
    Posts
    30
    Very nice. Thank you for your thoughts. Will try and convert to pthreads and see what I can do. Let you know how it works out.

  6. #6
    Registered User
    Join Date
    Sep 2008
    Posts
    30
    Ok, So Ive been fooling around with pthreads for quite a bit. They seem fairly simple for small programs, but for what im doing its getting a bit complicated and I was hoping someone good enlighten me here.

    So the client/server chat seems to be working as usual. Well actually less than usual.
    Its not sending any data to any clients at all now. Before it was only sending data to the client that sent it. I.E echoing it back to the original client. That was using fork(). Now that ive used pthreads Im having trouble getting it to echo back to anyone. Its getting the data from clients and running the for loop to send to all clients but its not sending it to any of them its just iterating through and the clients are not receiving anything. The only improvement is that the global variable sockets[][] that holds all address's for clients is being updated globally and accessible to all threads. Whereas with the fork() it was local to each process and could not be updated globaly. So each thread has the list of all clients to send to, its just not happening.

    I turned the fork() into a pthread and added a pthread to the client code as well for recv() data.
    The parent process, Im guessing is how you would refer to it, in the client reads in data from the user via fgets() and sends it to the server, the thread newThread contains a function that loops a recv() function that receives data from the server. When I say data I mean text messages in a char array. The server parent is constantly listening/accepting connections and when a connection is accepted that connection gets a newThread and its connection (socket) is added to the sockets[][] 2d array. The newThread for the server contains a function called doStuff() that recv() data from the connected sockets and forwards that data to all clients in the sockets[][] array via iteration in the form of a for loop.



    Here is the code.


    Code in green is not relevant you can skip over that its just setting up the connections.
    Code in red is pretty relevant
    Code in black is kind of relevant

    Server.c
    Code:
    /* server.c - code for example server program that uses TCP
    
    Jacob Mott*/
    
    #ifndef unix
    
    #define WIN32
    
    #include <windows.h>
    
    #include <winsock.h>
    
    #else
    
    #define closesocket close
    
    #include <sys/types.h>
    
    #include <sys/socket.h>
    
    #include <netinet/in.h>
    
    #include <stdlib.h>
    
    #include <netdb.h>
    
    #endif
    
    #include <stdio.h>
    
    #include <string.h>
    #include <pthread.h>
    
    #define PROTOPORT 5193 /* default protocol port number */
    #define BUFFERSIZE 256
    
    #define QLEN 6 /* size of request queue */
    
    int  socketIndex = 0;
    void *dostuff(void *socket);
    int sockets[BUFFERSIZE][BUFFERSIZE];
    
    /*------------------------------------------------------------------------
    
     * * Program: server
    
     * * port - protocol port number to use
    
     * * Note: The port argument is optional. If no port is specified,
    
     * * the server uses the default given by PROTOPORT.
    
     * *
    
     * *------------------------------------------------------------------------
    
     * */
    pthread_mutex_t accept_mutex;
    
    main(argc, argv)
    
    	int argc;
    
    	char *argv[];
    
    {
    
    	struct hostent *ptrh; /* pointer to a host table entry */
    
    	struct protoent *ptrp; /* pointer to a protocol table entry */
    
    	struct sockaddr_in sad; /* structure to hold server.s address */
    
    	struct sockaddr_in cad; /* structure to hold client.s address */
            int sd, sd2;
    	int port; /* protocol port number */
    
    	int alen; /* length of address */
    	pthread_t newThread;
    
    	
    
    #ifdef WIN32
    
    	WSADATA wsaData;
    
    	WSAStartup(0x0101, &wsaData);
    
    #endif
    
    	memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
    
    	sad.sin_family = AF_INET; /* set family to Internet */
    
    	sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
    
    	/* Check command-line argument for protocol port and extract */
    
    	/* port number if one is specified. Otherwise, use the default */
    
    	/* port value given by constant PROTOPORT */
    
    	if (argc > 1) { /* if argument specified */
    
    		port = atoi(argv[1]); /* convert argument to binary */
    
    	} else {
    
    		port = PROTOPORT; /* use default port number */
    
    	}
    
    	if (port > 0) /* test for illegal value */
    
    		sad.sin_port = htons((u_short)port);
    
    	else { /* print error message and exit */
    
    		fprintf(stderr,"bad port number &#37;s\n",argv[1]);
    
    		exit(1);
    
    	}
    
    	/* Map TCP transport protocol name to protocol number */
    
    	if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) {
    
    		fprintf(stderr, "cannot map \"tcp\" to protocol number");
    
    		exit(1);
    
    	}
    
    	/* Create a socket */
    
    	sd = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
    
    	if (sd < 0) {
    
    		fprintf(stderr, "socket creation failed\n");
    
    		exit(1);
    
    	}
    
    	/* Bind a local address to the socket */
    
    	if (bind(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
    
    		fprintf(stderr,"bind failed\n");
    
    		exit(1);
    
    	}
    	printf("waiting for clients to connect to chat\n");
    
    	/* Specify size of request queue */
    
    	if (listen(sd, QLEN) < 0) {
    
    		fprintf(stderr,"listen failed\n");
    
    		exit(1);
    
    	}
    	while(1) {
    
    	/* Main server  - accept and handle requests */
    
    		alen = sizeof(cad);
    
    		if ( (sd2=accept(sd, (struct sockaddr *)&cad, &alen)) < 0) {
    
    			fprintf(stderr, "accept failed\n");
    
    			exit(1);
    
    		}
    		
    		//printf("connection established with client %d\n", cad);
    		sockets[0][socketIndex] = sd2;
    		socketIndex++;		
    		pthread_create(&newThread, NULL, dostuff,(void *)&sd2);
    	}// end accept() loop
    	return 0;
    }// end main
    
    
    		void *dostuff (void *socket){
    		
    		char file_buffer[10000],f_buffer[1000],name[12], compareTo[1000]="#exit#";
    
    		int n, sock;
    		sock=*(int*)socket;		
    		char buf_recv[1000], buf_send[1000]; /* buffer for string the server sends */
    		pthread_detach(pthread_self());
    		
    		printf("connected!\n>");
    
    		fflush(stdout);
    		
    		while(1){	
    		n = recv(sock, buf_recv, sizeof(buf_recv), 0);
    		fflush(stdout);
    		buf_recv[n]='\0';
    		
    		if (strcmp(buf_recv, compareTo) == 0){
    		//printf("%s, disconnected!\n>", name);
    		//fflush(stdout);
    		printf("disconnected!\n>");
    		fflush(stdout);
    			exit(0);
    		}// end if
    
    		int i, sk;
    		for (i = 0; i<BUFFERSIZE; i++){
    		printf("socets[0][i] = %d\n ", sockets[0][i]);
    		fflush(stdout);
    		if ((sk = sockets[0][i]) == 0)
    		break;
    
    		else{
    		send(sk,buf_recv,strlen(buf_recv),0);
    		fflush(stdout);
    		}//end else
    
    		}//end for loop
    
    		
    		
    		}//end while loop		
    
    
    		exit(0);
    		}// end dostuff()
    Client.c
    Code:
    /* client.c - code for example client program that uses TCP
    
    Jacob Mott*/
    
    #ifndef unix
    
    #define WIN32
    
    #include <windows.h>
    
    #include <winsock.h>
    
    #else
    
    #define closesocket close
    
    #include <sys/types.h>
    
    #include <sys/socket.h>
    
    #include <netinet/in.h>
    
    #include <arpa/inet.h>
    
    #include <netdb.h>
    
    #endif
    
    #include <stdio.h>
    
    #include <string.h>
    
    #define PROTOPORT 5193 /* default protocol port number */
    
    char localhost[] = "localhost"; /* default host name */
    void *sendStuff(void *sock);
    
    /*------------------------------------------------------------------------
    
     * * Program: client
    
     * * Purpose: allocate a socket, connect to a server,
    
     * * Syntax: client [ host [port] ]
    
     * * host - name of a computer on which server is executing
    
     * * port - protocol port number server is using
    
     * * Note: Both arguments are optional. If no host name is specified,
    
     * * the client uses "localhost"; if no protocol port is
    
     * * specified, the client uses the default given by PROTOPORT.
    
     * *------------------------------------------------------------------------
    
     * */
    void chat(int);
    
    main(argc, argv)
    
    	int argc;
    
    	char *argv[];
    
    {
    
    	struct hostent *ptrh; /* pointer to a host table entry */
    
    	struct protoent *ptrp; /* pointer to a protocol table entry */
    
    	struct sockaddr_in sad; /* structure to hold an IP address */
    
    	int sd; /* socket descriptor */
    
    	int port; /* protocol port number */
    
    	char *host; /* pointer to host name */
    
    #ifdef WIN32
    
    	WSADATA wsaData;
    
    	WSAStartup(0x0101, &wsaData);
    
    #endif
    
    	memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
    
    	sad.sin_family = AF_INET; /* set family to Internet */
    
    	/* Check command-line argument for protocol port and extract */
    
    	/* port number if one is specified. Otherwise, use the default */
    
    	/* port value given by constant PROTOPORT */
    
    	if (argc > 2) { /* if protocol port specified */
    
    		port = atoi(argv[2]); /* convert to binary */
    
    	} else {
    
    		port = PROTOPORT; /* use default port number */
    
    	}
    
    	if (port > 0) /* test for legal value */
    
    		sad.sin_port = htons((u_short)port);
    
    	else { /* print error message and exit */
    
    		fprintf(stderr,"bad port number %s\n",argv[2]);
    
    		exit(1);
    
    	}
    
    	/* Check host argument and assign host name. */
    
    	if (argc > 1) {
    
    		host = argv[1]; /* if host argument specified */
    
    	} else {
    
    		host = localhost;
    
    	}
    
    	/* Convert host name to equivalent IP address and copy to sad. */
    
    	ptrh = gethostbyname(host);
    
    	if ( ((char *)ptrh) == NULL ) {
    
    		fprintf(stderr,"invalid host: %s\n", host);
    
    		exit(1);
    
    	}
    
    	memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);
    	/* Map TCP transport protocol name to protocol number. */
    	if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) {
    		fprintf(stderr, "cannot map \"tcp\" to protocol number");
    		exit(1);
    	}
    
    	/* Create a socket. */
    
    	sd = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
    
    	if (sd < 0) {
    
    		fprintf(stderr, "socket creation failed\n");
    
    		exit(1);
    
    	}
    
    	/* Connect the socket to the specified server. */
    
    	if (connect(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
    
    		fprintf(stderr,"connect failed\n");
    
    		exit(1);
    
    	}
    
    	/* Start chating*/
    	
    	chat(sd);
    	return 0;
    
    }
    
    	void chat(int sock){
    	pthread_t newThread;
    	int n, len; /* number of characters read */
    	char buf_recv[1000], buf_send[1000], name[15], compareTo[10]="#exit#",compareNull[1000]="\n";/* buffer for data from the server */
    
    	printf("connection established with server\n");
    	
    		pthread_create(&newThread,NULL,sendStuff,(void*)&sock);
    	while(1){
    		int gotit=0;
    		int ah;
    
    		while (gotit == 0){
    		fgets(buf_send, 1000, stdin);
    		if (strcmp(buf_send, compareNull) == 0){
    			printf("please enter text don't leave blank\n");
    			fflush(stdout);
    			gotit = -1;
    		}
    		gotit++;
    		}
    		
    		int aa = strlen(buf_send);
    		buf_send[aa-1]='\0';
    
    		send(sock, buf_send,strlen(buf_send),0);
    		fflush(stdout);
    		
    		if (strcmp(buf_send, compareTo) == 0)
    			exit(0);		
    
    		}//end while loop
    
    
    }
    void *sendStuff(void *sock){
    		int n, socket;
    		socket=*(int*)sock;
    		char buf_recv[1000], name[15];
    		pthread_detach(pthread_self());
    		while(1){
    		if ((n = recv(socket, buf_recv, sizeof(buf_recv), 0)) < 1);
    		return;
    		fflush(stdout);
    		buf_recv[n]='\0';
    
    
    		printf("%s\n",buf_recv);
    		fflush(stdout);
    		}
    		}//end sendstuff()
    
    
    Last edited by jakemott; 11-29-2008 at 06:27 PM.

  7. #7
    Registered User
    Join Date
    Sep 2008
    Posts
    30
    No worries, After a lot of hacking away at the code it was a simple fflush(stdout); line that wasn't present that was affecting the output to each client. Its working perfectly now. Perfect concurrent chat between multiple clients. Well not perfect but it works. lol. Thanks to pthreads. Thanks again matsp for pointing out forks() properties and pthreads. I definetly will be using pthreads alot in the future. The code below works if you just insert some fflush(stdouts) in the clients sendStuff() function. The buffer just wasn't being emptied. Time to make this code look pretty and add some more functions. Thanks again. Hope to contribute/use this forum much in the future.
    Last edited by jakemott; 11-30-2008 at 10:49 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Programming chat client, need some help (sockets & threads)
    By lalilulelo17 in forum Linux Programming
    Replies: 1
    Last Post: 04-19-2008, 04:01 AM
  2. Requesting Java Chat Client Maintenence
    By ygfperson in forum A Brief History of Cprogramming.com
    Replies: 6
    Last Post: 04-02-2003, 01:57 PM
  3. Starting window sockets
    By _Cl0wn_ in forum Windows Programming
    Replies: 2
    Last Post: 01-20-2003, 11:49 AM
  4. SO close to finishing chat program...!
    By Nakeerb in forum C++ Programming
    Replies: 13
    Last Post: 10-26-2002, 12:24 PM
  5. Rough Portable Chat Design Sketch
    By ggs in forum A Brief History of Cprogramming.com
    Replies: 0
    Last Post: 08-27-2001, 07:44 AM