2015-11-25 234 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