2012-12-05 61 views
2

嗨我正在爲網絡類創建一個項目,我們在網絡中創建節點,通過udp從控制應用程序接收消息,然後與其他人創建tcp連接節點。基本的順序是:1)控制管理器向節點A發送udp消息,告訴它連接到節點B 2)節點A接收到udp消息並將該udp消息轉發到節點B 3)節點B接收到udp消息,隨機選擇一個端口號碼,在該端口上打開一個偵聽的TCP套接字,併發送一個UDP端口號爲4的udp消息。節點A收到udp消息,並在該TCP端口上打開一個連接到節點B.理解選擇循環以接收UDP消息並打開TCP套接字

基本上,我想循環接收到的udp消息,並跳到我的消息解析函數,同時循環tcp連接。這部分看起來很簡單,但我並不真正瞭解如何將偵聽套接字添加到文件描述符列表中。下面的代碼是我放在一起,我想知道基本結構看起來是否正確?我真的不明白如何創建偵聽tcp套接字並將其添加到fd列表中?

SOCKET udpsock; 
udpsock = initudp(port); //setup udp socket 
SOCKET tcpsock; 
FD_ZERO(&rdsocks); 

max = udpsock + 1; 
while(1) 
{ 
    SOCKET temp; 

    FD_SET(udpsock,&rdsocks);//setup udp macros 
    FD_SET(tcpsock,&rdsocks); 

    if(select(max,&rdsocks,NULL,NULL,NULL) == SOCKET_ERROR) 
    { 
     perror("Select error"); 
     WSACleanup(); 
     return 1; 
    } 
    for(temp = 0; temp<=max;temp++) // loop on TCP sockets 
    { 
     if(FD_ISSET(temp,&rdsocks)) 
     { 
      printf("Socket %d is ready \n",temp); 
      // process tcp messages 
     } 

    } 
    if(FD_ISSET(udpsock,&rdsocks)) // udp connection, parse control message 
    { 
     int ret = 0; 
     res = recvfrom(udpsock, buff,sizeof(buff),0,(struct sockaddr*)&udpclient,lenaddr); 
     //process udp message, setup tcp connection here if requested and add to file descriptor list? 
    } 

} 

回答

1

在調用FD_SET()select()之前,您需要在每次循環迭代中調用FD_ZERO()。此外,在嘗試首先連接之前,請不要將TCP套接字添加到fd_set。保留您創建的TCP套接字列表,以便您可以在每次循環迭代時將它們重新添加到fd_set

嘗試這樣:

udpsock = initudp(port); //setup udp socket 

std::vector<SOCKET> tcpsocks; 
SOCKET tcpsock; 

while(1) 
{ 
    FD_ZERO(&rdsocks); 
    FD_SET(udpsock, &rdsocks); 
    max = udpsock; 

    for(size_t i = 0; i < tcpsocks.size(); ++i) 
    { 
     tcpsock = tcpsocks[i]; 
     FD_SET(tcpsock, &rdsocks); 

     if(tcpsock > udpsock) 
      max = tcpsock; 
    } 

    if(select(max+1, &rdsocks, NULL, NULL, NULL) == SOCKET_ERROR) 
    { 
     perror("Select error"); 
     WSACleanup(); 
     return 1; 
    } 

    for(size_t i = 0; i < tcpsocks.size(); ++i) // loop on TCP sockets 
    { 
     tcpsock = tcpsocks[i];    
     if(FD_ISSET(tcpsock, &rdsocks)) 
     { 
      printf("Socket %d is ready \n", tcpsock); 
      // process tcp message 
     } 
    } 

    if(FD_ISSET(udpsock, &rdsocks)) 
    { 
     int ret = 0; 
     res = recvfrom(udpsock, buff, sizeof(buff), 0, (struct sockaddr*)&udpclient, lenaddr); 
     // process udp message... 
     if(setup tcp connection is requested) 
     { 
      tcpsock = ...; 
      if(tcpsock != INVALID_SOCKET) 
       tcpsocks.push_back(tcpsock); 
     } 
    } 
} 
2

一旦建立監聽TCP套接字(socket(2)bind(2)listen(2)),標記用setsockopt(2)它非阻塞,並將其添加到讀設置select(2)

當它變得「可讀」時,表示您有客戶端連接掛起,請致電accept(2)。也將新連接的套接字添加到讀取集。

你可能想保持一個列表/散列/不管這些客戶端套接字,因爲讀集有每次調用select(2)前必須重新初始化並計算其第一個參數(最大FD)。

注0:我參考了這裏的Linux手冊頁,但邏輯幾乎是跨平臺的。您可以在MSDN上找到Windows參考。 注1:Windows select() IGNORES的第一個參數。