2015-11-25 311 views
2

我想使用C++使用TCP協議製作服務器和客戶端程序。服務器必須能夠一次處理多個客戶端。但問題是,例如,啓動服務器後,我運行2臺客戶端,並將服務器的IP地址和端口作爲參數。接下來,兩個客戶端都向服務器發送數據。起初,兩個客戶端都可以向服務器發送數據,服務器可以讀取數據。但是,一旦服務器從第二個客戶端收到數據,它似乎停止從第一個客戶端接收數據。你有任何解決方案?C++多客戶端TCP服務器

這裏是服務器端的代碼

using namespace std; 

void *task1(void *); 

static int connFd; 
void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

int main(int argc, char* argv[]) 
{ 
    int pId, portNo, listenFd; 
    socklen_t len; //store size of the address 
    bool loop = false; 
struct sockaddr_in svrAdd, clntAdd; 

pthread_t threadA[3]; 

if (argc < 2) 
{ 
    cerr << "Syntam : ./server <port>" << endl; 
    return 0; 
} 

portNo = atoi(argv[1]); 

if((portNo > 65535) || (portNo < 2000)) 
{ 
    cerr << "Please enter a port number between 2000 - 65535" << endl; 
    return 0; 
} 

//create socket 
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 

if(listenFd < 0) 
{ 
    cerr << "Cannot open socket" << endl; 
    return 0; 
} 

bzero((char*) &svrAdd, sizeof(svrAdd)); 

svrAdd.sin_family = AF_INET; 
svrAdd.sin_addr.s_addr = INADDR_ANY; 
svrAdd.sin_port = htons(portNo); 

//bind socket 
if(bind(listenFd, (struct sockaddr *)&svrAdd, sizeof(svrAdd)) < 0) 
{ 
    cerr << "Cannot bind" << endl; 
    return 0; 
} 

    listen(listenFd, 5); 

    int noThread = 0; 

    while (noThread < 3) 
    { 
     socklen_t len = sizeof(clntAdd); 
     cout << "Listening" << endl; 

    //this is where client connects. svr will hang in this mode until   client conn 
     connFd = accept(listenFd, (struct sockaddr *)&clntAdd, &len); 

     if (connFd < 0) 
     { 
      cerr << "Cannot accept connection" << endl; 
      return 0; 
     } 
     else 
     { 
      cout << "Connection successful" << endl; 
     } 

     pthread_create(&threadA[noThread], NULL, task1, NULL); 

     noThread++; 
    } 

    for(int i = 0; i < 3; i++) 
    { 
     pthread_join(threadA[i], NULL); 
    } 
} 

void *task1 (void *dummyPt) 
{ 
    cout << "Thread No: " << pthread_self() << endl; 
    char test[256]; 
    bzero(test, 256); 
    bool loop = false; 
    while(!loop) 
    {  
     bzero(test, 256);  
     int n = read(connFd, test, 255); 
     if (n < 0) error("ERROR reading from socket"); 
     printf("Here is the message: %s\n",test);    
    } 
    cout << "\nClosing thread and conn" << endl; 
    close(connFd); 
} 

而且客戶端代碼

using namespace std; 

int main (int argc, char* argv[]) 
{ 
    int listenFd, portNo; 
    bool loop = false; 
    struct sockaddr_in svrAdd; 
    struct hostent *server; 

    if(argc < 3) 
    { 
     cerr<<"Syntax : ./client <host name> <port>"<<endl; 
     return 0; 
    } 

    portNo = atoi(argv[2]); 

    if((portNo > 65535) || (portNo < 2000)) 
    { 
     cerr<<"Please enter port number between 2000 - 65535"<<endl; 
     return 0; 
    }  

    //create client skt 
    listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 

    if(listenFd < 0) 
    { 
     cerr << "Cannot open socket" << endl; 
     return 0; 
    } 

    server = gethostbyname(argv[1]); 

    if(server == NULL) 
    { 
     cerr << "Host does not exist" << endl; 
     return 0; 
    } 

    bzero((char *) &svrAdd, sizeof(svrAdd)); 
    svrAdd.sin_family = AF_INET; 

    bcopy((char *) server -> h_addr, (char *) &svrAdd.sin_addr.s_addr, server -> h_length); 

    svrAdd.sin_port = htons(portNo); 

    int checker = connect(listenFd,(struct sockaddr *) &svrAdd, sizeof(svrAdd)); 

    if (checker < 0) 
    { 
     cerr << "Cannot connect!" << endl; 
     return 0; 
    } 

    //send stuff to server 
    for(;;) 
    { 
     char s[300]; 
     //cin.clear(); 
     //cin.ignore(256, '\n'); 
     cout << "Enter stuff: "; 
     bzero(s, 300); 
     cin.getline(s, 300); 

     write(listenFd, s, strlen(s)); 
    } 
} 
+1

你有一個答案,你原來的問題。試圖實現該答案會導致您獲得新的錯誤。您不應該通過添加錯誤來損害您的帖子,這些錯誤在仍處於帖子中的原始問題的上下文中是沒有意義的。相反,發佈錯誤的新問題。我已經編輯了這個問題,回答如何保持答案的完整性 – JBentley

回答

4

尤爾connFd是一個全局變量,你從你的主線程和所有的處理線程訪問。這不會做!想象一下 - 你已經接受了第一個連接並將變量設置爲接收套接字。你已經產生了開始閱讀的處理線程。接下來的事情你知道,另一個連接即將到來,你也收到它!這一刻connFd指向新的連接,所以已經使用它的線程將突然切換到新的連接!當然這不好。

解決此問題的方法是將連接傳遞給線程,以便不會跨線程共享。最簡單的方法是使用C++線程類。

例如,這是說明上述想法的代碼片段:

void handle_connection(int fd) { 
    ... <your task1 code> 
} 

... 
std::vector<std::thread> threads; 
... 
int conn = accept(listenFd, (struct sockaddr *)&clntAdd, &len); 
threads.push_back(std::thread(&handle_connection, conn)); 
... 

... (in the end) 
for (auto&& t : threads) 
    t.join(); 
+0

實施你的建議後,我得到了一些錯誤。請看我的更新。 – ZZZ