2010-12-01 84 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