2010-12-01 67 views
0

我正在使用套接字聊天应用程序,即我想一次与多个客户端聊天。我写了下面的程序。服务器接受多个客户端,但我只能与最新的客户端聊天。我不能和以前的客户聊天,有人可以向我解释为什么?套接字聊天应用程序,即我想同时与多个客户端聊天?

/* tcpserver.c */ 

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 
#include <pthread.h> 

void *thread(int *); 

int main() 
{ 
     int sock, connected, true = 1,n=1; 
    pthread_t tid; 
     struct sockaddr_in server_addr,client_addr; 
     int sin_size; 

     if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
      perror("Socket"); 
      exit(1); 
     } 

     if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int)) == -1) { 
      perror("Setsockopt"); 
      exit(1); 
     } 

     server_addr.sin_family = AF_INET;   
     server_addr.sin_port = htons(5000);  
     server_addr.sin_addr.s_addr = INADDR_ANY; 
     bzero(&(server_addr.sin_zero),8); 

     if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) 
                     == -1) { 
      perror("Unable to bind"); 
      exit(1); 
     } 

     if (listen(sock, 5) == -1) { 
      perror("Listen"); 
      exit(1); 
     } 

    printf("\nTCPServer Waiting for client on port 5000"); 
     fflush(stdout); 


     while(n<=5) 
     { 

      sin_size = sizeof(struct sockaddr_in); 

      connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size); 

      printf("\n I got a connection from (%s , %d)", 
        inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); 
     pthread_create(&tid,NULL,thread,&connected); 
     n++; 
    } 

     close(sock); 
     return 0; 
} 


void *thread(int *nfd) 
{ 
     char send_data [1024] , recv_data[1024];  
     int bytes_recieved; 
     while (1) 
      { 
       printf("\n SEND (q or Q to quit) : "); 
       gets(send_data); 

       if (strcmp(send_data , "q") == 0 || strcmp(send_data , "Q") == 0) 
       { 
       send(*nfd, send_data,strlen(send_data), 0); 
       close(nfd); 
       break; 
       } 

       else 
       send(*nfd, send_data,strlen(send_data), 0); 

       bytes_recieved = recv(*nfd,recv_data,1024,0); 

       recv_data[bytes_recieved] = '\0'; 

       if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0) 
       { 
       close(*nfd); 
       break; 
       } 

       else 
       printf("\n RECIEVED DATA = %s " , recv_data); 
       fflush(stdout); 
      } 

} 







/* tcpclient.c */ 

#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 


int main() 

{ 

     int sock, bytes_recieved; 
     char send_data[1024],recv_data[1024]; 
     struct hostent *host; 
     struct sockaddr_in server_addr; 

     host = gethostbyname("127.0.0.1"); 

     if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
      perror("Socket"); 
      exit(1); 
     } 

     server_addr.sin_family = AF_INET;  
     server_addr.sin_port = htons(5000); 
     server_addr.sin_addr = *((struct in_addr *)host->h_addr); 
     bzero(&(server_addr.sin_zero),8); 

     if (connect(sock, (struct sockaddr *)&server_addr, 
        sizeof(struct sockaddr)) == -1) 
     { 
      perror("Connect"); 
      exit(1); 
     } 

     while(1) 
     { 

      bytes_recieved=recv(sock,recv_data,1024,0); 
      recv_data[bytes_recieved] = '\0'; 

      if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0) 
      { 
      close(sock); 
      break; 
      } 

      else 
      printf("\nRecieved data = %s " , recv_data); 

      printf("\nSEND (q or Q to quit) : "); 
      gets(send_data); 

      if (strcmp(send_data , "q") != 0 && strcmp(send_data , "Q") != 0) 
      send(sock,send_data,strlen(send_data), 0); 

      else 
      { 
      send(sock,send_data,strlen(send_data), 0); 
      close(sock); 
      break; 
      } 

     } 
return 0; 
} 

回答

0

这是一个技巧性的问题,对吗?

您正在将“已连接”的地址传递给线程,而不是值。一旦有新的连接进入,该值将被覆盖,并且最终有两个线程正在与同一个连接进行通话。

顺便说一句,为什么你使用多线程? poll()是更好的解决方案。您应该可以轻松地构建一个可以使用单个线程处理多个客户端的服务器。这真的不难。

此外,如果你想要一个真正的多人聊天应用程序,看看多重铸造。这意味着你只需要一次写一条消息,而不是多次,每个套接字一次。我自己没有做过任何多重演员,所以不能再告诉你。

+0

我很努力地不给你一个完整的代码审查,以免我伤害你的感受,但你意识到服务器不会看到什么客户端类型,直到它发送了一些东西给客户端?此外,当你键入你有多个线程(可能)所有从标准输入读取同时。我不寒而栗,想想那里会发生什么。我猜测,当你在服务器端输入一些东西时,只有一个客户端(根据线程调度选择)将接收数据。 – AlastairG 2010-12-01 11:52:25

2

其他帖子将帮助您调试当前的错误。如果您决定扩展项目而不是基本的服务器 - 客户端模型,此建议将有望为您节省时间和耐心。

建议是:不要使用线程。民意调查()需要大量的资源。使用select()。

当你真的需要使用它们时应该使用线程。 John Ousterhout说明了this很久以前,由于某种原因,我总是记得当人们失去调试基本线程行为时。

alt text

0

(不是一个真正的答案:)当你想多处理器机器时,才应使用多个线程同时将利用其不同的处理器。例如,绘制复杂的数学图时,您可能会将图分成几个部分,以便每个处理器可以同时计算其中的不同部分。许多人可能不同意,但如果你需要多线程,因为你想打开I/O你做错了。

在这里,你想要多线程只是因为你想解除I/O。一种更简单的方法是将服务器fork()到子进程中,以便将消息简单写入其指定的套接字。
更好的方法是Alastair提到的多点投射。但是多播也可以做得不好。 This is a good text on it