Thread: Chat Program Help?

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    167

    Chat Program Help?

    I have two files: client.c and a server.c. You can type a line on the client, press enter, and it will be displayed on the server. Then type a line on the server, press enter, and it gets displayed on the client side.

    The problem is when you type two messages on either side. Then it gets buffered in memory, but won't display until after you type something on the other side. Like this:

    Client: type "Hello"
    Client: type "Hello2"

    Server shows: "Hello"

    <Hello2 does not show>

    Server: type "Hi there"

    Server shows: "Hello2" (shows only after typing Hi there and pressing enter)

    (The problem is exacerbated if one side types 3 or more lines. It all gets buffered and won't display until the other side types.)
    Does anyone know how I could get around this problem? More of a conceptual problem I suppose.

    -----

    Here is my source code and a makefile (I put red/bold around the relevant while loops, the only areas I'm coding in):

    Code:
    CC = /usr/bin/g++
    CFLAGS = -Wall -pthread -lpthread
    LIBS = -lm
    
    APP = client server
    OBJS =
    
    all: $(APP)
    $(APP): $(OBJS)
    	$(CC) $(CFLAGS) -g -o $@ $(OBJS) $(LIBS)
    
    client: client.c
    	$(CC) $(CFLAGS) -g -o client client.c
    
    server: server.c
    	$(CC) $(CFLAGS) -g -o server server.c
    
    clean:
    	rm -f *.o *~* $(APP)
    ./server 3200 // (random port #)
    ./client 127.0.0.1 3200 // (localhost ip and random port#)

    Code:
    // Client Program
    #include <stdio.h>
    #include <sys/socket.h>
    #include <arpa/inet.h> /*for sockaddr_in and inet_addr()*/ // V
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <netinet/in.h>
    
    #define BUFFSIZE 256
    #define SERVER_PORT 6401
    
    int main(int argc, char** argv)
    {
    	int sock;
    	struct sockaddr_in echoserver;
    	char buffer[BUFFSIZE]={0};
    	unsigned int echolen;
    
    	int command = 0;
    	char input[100];
    
    	int received = 0;
    
    	if(argc < 3)
    	{
    		printf("Usage : cli <serverip> <String> \n");
    		exit(1);
    	}
    
    	if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    	{
    		perror("Failed to create socket \n");
    		exit(0);
    	}
    
    	memset(&echoserver, 0, sizeof(echoserver));
    	echoserver.sin_family = AF_INET;
    	echoserver.sin_addr.s_addr = inet_addr(argv[1]);
    	echoserver.sin_port = htons(SERVER_PORT);
    	if(connect(sock, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0)
    	{
    		perror("Failed to connect with the server \n");
    		exit(0);
    	}
    
    	memcpy(buffer,argv[2],sizeof(buffer)-1);
    	echolen = strlen(buffer);
    
    	while (command != 3 && command != 4)
    	{
    		printf("Enter option (1 - 4): ");
    		fgets(input, 100, stdin);
    		command = atoi(input);
    
    		if (command == 1)
    		{
    			while(input[0] != 'E' || input[1] != 'X' || input[2] != 'I' || input[3] != 'T')
    			{
    				fgets(input, 100, stdin);
    				if (send(sock, input, sizeof(input), 0) == -1)
    					perror("send error");
    
    				if(( received = recv(sock,buffer,BUFFSIZE-1, 0)) < 0 )
    				{
    					perror("Failed to receive additional bytes from client \n");
    				}
    				if(received >0)
    				{
    					printf("Received from Server : &#37;s",buffer);
    				}
    			}
    		}
    		else if (command == 2)
    		{
    			printf("Do 2\n");
    			// Client sends url to server in format http://<server name>/
    			// Server downloads index.html and sends to client's local directory
    			// File saved as index.html (over-write if necessary)
    		}
    		else if (command == 3)
    		{
    			printf("Thank You.\n");
    			// Disconnect Client
    		}
    		else if (command == 4)
    		{
    			printf("Thank You Very Much.\n");
    			// Disconnect Client and Server
    		}
    		else
    		{
    			printf("Improper input.\n");
    		}
    	}
    
    	close(sock);
    }
    Code:
    // Server Program
    #include <stdio.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #define BUFFSIZE 256
    #define MAX_PENDING 10
    #define MAX_CLIENTS MAX_PENDING
    
    pthread_t clients[MAX_CLIENTS];
    #define SERVER_PORT 6401
    
    void *ClientData(void* arg)
    {
    	int sock = *(int*)arg;
    	char buffer[BUFFSIZE] = {0};
    	int received = -1;
    
    	fd_set fds;
    	int maxfd;
    	int nready;
    
    	char input[100];
    
    	maxfd = sock + 1;
    
    	while(1)
    	{
    		FD_ZERO(&fds);
    		FD_SET(sock,&fds);
    
    		nready = select(maxfd,&fds,0,0,0);
    		if(FD_ISSET(sock,&fds))
    		{
    			if(( received = recv(sock,buffer,BUFFSIZE-1, 0)) <0 )
    			{
    				perror("Failed to receive additional bytes from client \n");
    			}
    			if(received >0)
    			{
    				printf("Received from Client : %s",buffer);
    			}
    			if(received < 1)
    			{
    				printf("Closing this client\n");
    				close(sock);
    				pthread_exit(NULL);
    			}
    			memset(buffer,0,sizeof(buffer)); // V, 0
    		}
    		fgets(input, 100, stdin);
    		if (send(sock, input, sizeof(input), 0) == -1)
    			perror("send error");
    	}
    	close(sock);
    }
    
    int main()
    {
    	int threadCount =0;
    
    	int sock,clientsock;
    	struct sockaddr_in echoserver,echoclient;
    	char buffer[BUFFSIZE] = {0};
    
    	if(( sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0 )
    	{
    		perror("Failed to created socket \n");
    	}
    
    	memset(&echoserver, 0, sizeof(echoserver));
    	echoserver.sin_family = AF_INET;
    	echoserver.sin_addr.s_addr = htonl(INADDR_ANY);
    	echoserver.sin_port = htons(SERVER_PORT);
    
    	if(bind(sock,(struct sockaddr *) &echoserver, sizeof(echoserver)) < 0)
    	{
    		perror("Failed to bind the socket \n");
    	}
    	if(listen(sock, MAX_PENDING) < 0)
    	{
    		perror("Failed to listen on server socket \n");
    	}
    
    	while(1)
    	{
    		unsigned int clientlen = sizeof(echoclient);
    
    		if(( clientsock = accept(sock, (struct sockaddr*) &echoclient, &clientlen)) < 0)
    		{
    			perror("Failed to accept client connection \n");
    		}
    		fprintf(stdout,"Client Connected : %s \n",inet_ntoa(echoclient.sin_addr));
    
    		if(clientsock > -1)
    		{
    			if(threadCount<MAX_CLIENTS)
    				pthread_create(&clients[threadCount++],NULL,ClientData,(void *) &clientsock);
    		}
    	}
    }

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Using fgets at the end of the while (1) loop in the server will guarantee that the loop is paused and waiting for local input before it checks the socket again. These two processes cannot really be in the same loop.
    Last edited by MK27; 11-12-2008 at 12:45 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    Apr 2008
    Posts
    167
    Quote Originally Posted by MK27 View Post
    Using fgets at the end of the while (1) loop in the server will guarantee that the loop is paused and waiting for local input before it checks the socket again.
    Is there another function I can use?

    I thought about using scanf but that will yield the same problem.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    The problem is not that you use fgets. The problem is that you use fgets at the end of the while loop. select can be made to check for input from a file descriptor and, if there is none, move on. But fgets cannot. If you call fgets, it is going to wait for input.

    I'm no pro, I don't use windows, and I just started with c in the summer, but I have written an ncurses forking (local socket) server and client which works well (you can send messages back and forth without this problem, etc). Unfortunately, I haven't gotten into pthreads yet, so I'm not sure of the syntax, but logically YOU MUST seperate the process which waits for input from the socket from the process which waits for input locally. With fork, this means you have the child in the server monitoring connected sockets via select, while the parent waits for keyboard input and sends it. With pthreads, I suspect you must use seperate function calls. Hopefully someone out there can fill in the gaps for us.

    The key point: once you call fgets, or scanf, or whatever you want to use for local input, that command is going to wait until there is local input, and nothing else will happen. You have to use seperate, parallel loops.

    If you don't have much experience with pthreads you should use fork until you get this working. I found the use of sockets for inter-process communication very tricky, relatively speaking, and I suspect you still have a long way to go but you are getting there.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    Apr 2008
    Posts
    167
    Quote Originally Posted by MK27 View Post
    The problem is not that you use fgets. The problem is that you use fgets at the end of the while loop. select can be made to check for input from a file descriptor and, if there is none, move on. But fgets cannot. If you call fgets, it is going to wait for input.

    I'm no pro, I don't use windows, and I just started with c in the summer, but I have written an ncurses forking (local socket) server and client which works well (you can send messages back and forth without this problem, etc). Unfortunately, I haven't gotten into pthreads yet, so I'm not sure of the syntax, but logically YOU MUST seperate the process which waits for input from the socket from the process which waits for input locally. With fork, this means you have the child in the server monitoring connected sockets via select, while the parent waits for keyboard input and sends it. With pthreads, I suspect you must use seperate function calls. Hopefully someone out there can fill in the gaps for us.

    The key point: once you call fgets, or scanf, or whatever you want to use for local input, that command is going to wait until there is local input, and nothing else will happen. You have to use seperate, parallel loops.

    If you don't have much experience with pthreads you should use fork until you get this working. I found the use of sockets for inter-process communication very tricky, relatively speaking, and I suspect you still have a long way to go but you are getting there.
    Ok so I took your advice and started researching some fork() stuff. I found this page which had a little example on it: http://www.linuxhowtos.org/C_C++/socket.htm

    So I made the modifications to my program exactly like the page above. It compiles and runs just fine, but unfortunately, it seems it didn't make any difference at all.

    Am I doing it wrong?

    Here's what I did:

    Code:
    //Client
    		if (command == 1)
    		{
    			while(input[0] != 'E' || input[1] != 'X' || input[2] != 'I' || input[3] != 'T')
    			{
    				int pid = fork();
    				if (pid < 0)
    					perror("fork error");
    				if (pid == 0)
    				{
    					fgets(input, 100, stdin);
    					if (send(sock, input, sizeof(input), 0) == -1)
    						perror("send error");
    				}
    
    				if(( received = recv(sock,buffer,BUFFSIZE-1, 0)) < 0 )
    				{
    					perror("Failed to receive additional bytes from client \n");
    				}
    				if(received >0)
    				{
    					printf("Received from Server : &#37;s",buffer);
    				}
    			}
    		}
    Code:
    //Server
    	while(1)
    	{
    		int pid;
    		FD_ZERO(&fds);
    		FD_SET(sock,&fds);
    
    		nready = select(maxfd,&fds,0,0,0);
    		if(FD_ISSET(sock,&fds))
    		{
    			if(( received = recv(sock,buffer,BUFFSIZE-1, 0)) <0 )
    			{
    				perror("Failed to receive additional bytes from client \n");
    			}
    			if(received >0)
    			{
    				printf("Received from Client : %s",buffer);
    			}
    			if(received < 1)
    			{
    				printf("Closing this client\n");
    				close(sock);
    				pthread_exit(NULL);
    			}
    			memset(buffer,0,sizeof(buffer)); // V, 0
    		}
    
    		pid = fork();
    		if (pid < 0)
    			perror("fork error");
    		if (pid == 0)
    		{
    			fgets(input, 100, stdin);
    			if (send(sock, input, sizeof(input), 0) == -1)
    				perror("send error");
    		}
    	}

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    First off, you are using fork kind of wrong, but bare with me a minute...

    You are using linux, great. You can look at my ncurses local socket version:

    http://www.intergate.com/~halfcountp...t_ncurses.html
    the header is http://www.intergate.com/~halfcountp...de/c/mine.html

    The server actually doesn't use fork because it uses the ncurses main loop, but the client does (the client isn't ncurses).and a simpler single command line program:

    http://www.intergate.com/~halfcountp...al-socket.html
    may be better to look at and doesn't require the header but it's one program instead of a seperate client/server.

    but really these are similar to many examples that are available on the web already, so I won't get nitty gritty about how they work unless you ask a specific question I may be online for another hour or so and again tomorrow.

    Anyway, the deal with fork is that after the call you need two or three ifs: one (sort of optional) to check for an error, but then one (==0, the child) to do the socket or input check
    and THEN ANOTHER ONE (==pid) which is the parent to do the parallel operation. The way you have it, fork is inside a while (1) loop. You should have the while loop inside the fork instead.

    Like I said, getting it all to work seemed very frustrating.
    Last edited by MK27; 11-12-2008 at 03:31 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User valaris's Avatar
    Join Date
    Jun 2008
    Location
    RING 0
    Posts
    507
    fork() returns twice. Once in the parent and once in the child. So if fork returns 0 you are in the child process that is now competing for a time slice of a CPU. Greater then zero will be the parent. So these two blocks are executing "concurrently", or at least appearing to.

  8. #8
    Registered User valaris's Avatar
    Join Date
    Jun 2008
    Location
    RING 0
    Posts
    507
    It could if you have multiple processors. Otherwise it competes for a time quantum to execute on. To a user though this could appear as working concurrently. No a while loop wont effect your fork(). It's a seperate process just like the original program you executed was a process. You can do anything with it .

    In fact in *nix essentially everytime you init a new program a new process is forked from the shell.

  9. #9
    Registered User valaris's Avatar
    Join Date
    Jun 2008
    Location
    RING 0
    Posts
    507
    Be wary that you get input and send it before you backwardsly check for exit. Consider strcmp(). Also you only try and recv once, everytime command == 1.

  10. #10
    Registered User
    Join Date
    Feb 2009
    Location
    Philippines
    Posts
    3
    hello!
    Thanks for the advice, MK27! what i'm worrying is that i'm running out of time in studying ncurses..But i guess i should try my best to learn it for a short period of time..

    I've tried compiling your code in http://www.intergate.com/~halfcountp...de/c/mine.html, but there's an error such as "undefine reference....

    what i did is use..."gcc -o m mine.c -lncurses" in compiling where i am wrong?

    can u provide me with sample code showing the txt I inputted from one window then showing the text i input in other window...I'll start studying with your sample I guess.

    THANKS !

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Program Plan
    By Programmer_P in forum C++ Programming
    Replies: 0
    Last Post: 05-11-2009, 01:42 AM
  2. my server program auto shut down
    By hanhao in forum Networking/Device Communication
    Replies: 1
    Last Post: 03-13-2004, 10:49 PM
  3. insufficient memory for tsr
    By manmohan in forum C Programming
    Replies: 8
    Last Post: 01-02-2004, 09:48 AM
  4. Date program starts DOS's date
    By jrahhali in forum C++ Programming
    Replies: 1
    Last Post: 11-24-2003, 05:23 PM
  5. My program, anyhelp
    By @licomb in forum C Programming
    Replies: 14
    Last Post: 08-14-2001, 10:04 PM