2012-11-23 58 views
1

我一直在寻找这个几个小时。我尝试了所有我能想到的事情,坦率地说,这没有意义。我主动发送和接收没有问题的套接字,但只要我将数据更改为不同的消息,相同的样式,就会停止接收。我正在使用TCP。我有一个经理进程发送N个路由器消息与表数据。我后来发送一个数据包,风格相同,接收它,然后停止接收....代码回到循环的顶部,但只是没有得到更多的数据。C++套接字只接收第一次发送?

哦,我使用的网络代码是beejs TCP服务器客户端代码的复制和粘贴。 http://beej.us/guide/bgnet/output/html/multipage/clientserver.html

管理器线程,这部分工作

for(vector< vector<int> >::iterator it = table.begin(); it!=table.end(); ++it){ 
      vector<int> d = *it; 

      for(vector<int>::iterator itA = d.begin(); itA!=d.end(); ++itA){ 
       cout << "Sending... "<< *itA << endl; 
       s <<*itA<<" "; 
      } 
      if (send(new_fd, s.str().c_str(), 13, 0) == -1) 
       perror("Serv:send"); 
      sleep(2); 
      logs << "Sent to router " << i <<":\n" << s.str(); 
      writeLog(logs.str().c_str()); 
      s.str(""); 
      logs.str(""); 


     } 
     s<<"done"; 
     if (send(new_fd, s.str().c_str(), 13, 0) == -1) 
      perror("Serv:send"); 
     writeLog(s.str().c_str()); 

管理2,其中只有第一条消息获得通过

for(vector <vector <int > >::iterator it = toSendPackets.begin(); it != toSendPackets.end(); ++it){ 
    sleep(3); 
    vector<int> tsp = *it; 
    int a,b,c = 0; 
    for(vector<int>::iterator itr = tsp.begin(); itr != tsp.end(); ++itr){ 
     if(c==0){ 
      a = *itr; 
     } 
     if(c==1){ 
      b = *itr; 
     } 

     c++; 
    } 
    ss.str(""); 
    ss << a << " " << b; 
    for(int i = 0; i < numN; i++){ 
     int curSoc = socketList[i]; 
     stringstream sl; 

     sl<<"sent:"<< ss.str().c_str(); 
     cout << "sending.. " << ss.str() << " to " << i << endl; 
     if (send(curSoc, "HOP", strlen("HOP")+1, 0) == -1) 
      perror("Serv:send"); 
     sleep(2); 
     if (send(curSoc, ss.str().c_str(), strlen(ss.str().c_str())+1, 0) == -1) 
      perror("Serv:send"); 
     writeLog(sl.str().c_str()); 
     sleep(1); 

    } 
} 

路由器代码。

上面的管理员代码和管理员代码2都发送到这部分代码。 它获得第一次发送,在这种情况下“HOP”,然后什么都没有?我删除了HOP数据包解析,因此它应该只说明某些内容已被读取。

if(tid == 0){// TCP 
    stringstream s; 
    bool proc = true; 
    while(!doneFlag){ 
     proc = true; 
     cout << "TCP RECEIVING... " << endl; 
     int numbytes = 0; 
     while(numbytes==0){ 
      if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { 
       perror("recvROUTERThread0"); 
       exit(1); 
      } 
     } 
     buf[numbytes] = '\0'; 
     numbytes = 0; 
     if(strcmp("Quit",buf)==0){ 
      writeLog("Quit read",outName); 
      doneFlag = true; 
      close(net.sockfd); 
      floodUDP("Quit"); 
      pthread_exit(NULL); 

     } 
     else if(strcmp("HOP",buf)==0){ 
      cout << "HOP READ" << endl; 
      numbytes = 0; 
      while(numbytes==0){ 
       if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { 
        perror("recvROUTERThread0"); 
        exit(1); 
       } 
      } 
      s << id << "R: Receiving a routing command! " << buf; 
      cout << s.str().c_str() << endl; 
      writeLog(s.str().c_str(),outName); 
      HOPpacket hpo = genHopOrig(s.str().c_str()); 
      if(hpo.s == atoi(id)){ 
       printHOP(hpo); 
       //    cout << "PACKET " << pr << endl; 
       stringstream sl; 
       char* hop = generateHopPacket(hpo); 
       sl << "Generating HOP packet and sending.. " << hop; 
       writeLog(sl.str().c_str(),outName); 
       sendHOP(hop); 
      } 
     } 
     else{ 
      cout << "Table row data from manager" << endl; 
      s.str(""); 
      s << id << "R: MANAGER MESSAGE: " << buf << endl; 
      cout << s.str() << endl; 
      writeLog(s.str().c_str(),outName); 
      int intID = atoi(id); 
      vector <int> tr = processTR(buf,intID,basePN); 
      table.push_back(tr); 
     } 

    } 
    } 

我的输出。在这种情况下,有10个路由器正在运行。注意:我没有改变我的印地指出,这是发送HOP然后0 5 ..

sending.. 0 5 to 0 

HOP READ 
WRITTING Manager log:12-11-23::4:6:26: 
sent:0 5 

sending.. 0 5 to 1 
HOP READ 
WRITTING Manager log:12-11-23::4:6:29: 
sent:0 5 

sending.. 0 5 to 2 
HOP READ 
WRITTING Manager log:12-11-23::4:6:32: 
sent:0 5 

sending.. 0 5 to 3 
HOP READ 
WRITTING Manager log:12-11-23::4:6:35: 
sent:0 5 

sending.. 0 5 to 4 
HOP READ 
WRITTING Manager log:12-11-23::4:6:38: 
sent:0 5 

sending.. 0 5 to 5 
HOP READ 
WRITTING Manager log:12-11-23::4:6:41: 
sent:0 5 

sending.. 0 5 to 6 
HOP READ 
WRITTING Manager log:12-11-23::4:6:44: 
sent:0 5 

sending.. 0 5 to 7 
HOP READ 
WRITTING Manager log:12-11-23::4:6:47: 
sent:0 5 

sending.. 0 5 to 8 
HOP READ 
WRITTING Manager log:12-11-23::4:6:50: 
sent:0 5 

sending.. 0 5 to 9 
HOP READ 
WRITTING Manager log:12-11-23::4:6:53: 
sent:0 5 

sending.. 3 9 to 0 
WRITTING Manager log:12-11-23::4:6:59: 
sent:3 9 

sending.. 3 9 to 1 
WRITTING Manager log:12-11-23::4:7:2: 
sent:3 9 

sending.. 3 9 to 2 
WRITTING Manager log:12-11-23::4:7:5: 
sent:3 9 

sending.. 3 9 to 3 
WRITTING Manager log:12-11-23::4:7:8: 
sent:3 9 

sending.. 3 9 to 4 
WRITTING Manager log:12-11-23::4:7:11: 
sent:3 9 

sending.. 3 9 to 5 
WRITTING Manager log:12-11-23::4:7:14: 
sent:3 9 

sending.. 3 9 to 6 
WRITTING Manager log:12-11-23::4:7:17: 
sent:3 9 

sending.. 3 9 to 7 
WRITTING Manager log:12-11-23::4:7:20: 
sent:3 9 

sending.. 3 9 to 8 
WRITTING Manager log:12-11-23::4:7:23: 
sent:3 9 

sending.. 3 9 to 9 
WRITTING Manager log:12-11-23::4:7:26: 
sent:3 9 
+0

你发送的使用( )不健壮;您需要检查返回码,如果该值不是您想要发送的字节数,可能会重试。 – tmyklebu

+0

您在其他代码片段中使用recv()和buf也是有问题的。你的代码无法发出你所引用的输出。 – tmyklebu

+0

它是控制台的直接复制和粘贴,但我可能没有粘贴所有的写入日志调用。 – L4nce0

回答

2

还有就是当你recv数据,TCP不是基于消息的,所以如果基于流的插座有问题使用:

send(sock, buf1, len1, 0); // Send HOP, since it is small, you OS merge this 
send(sock, buf2, len2, 0); // with next send! 

,然后尝试接收使用recv数据也不能保证您在2个独立的呼叫接收数据recv,所以你可以在一个呼叫同时接收发送缓冲区recv

recv(sock, buf, len, 0); // This may receive both buffers in one call 

因此您对recv的下一个呼叫将被阻止,以便在第一个呼叫中已经收到的数据!当发送大缓冲区时,它们也可能是另一个问题,那么recv可能会收到比使用send传递的单个消息更少的数据。

您必须定义一个协议来定义接收到的流中的消息结尾,然后根据该协议接收您的数据。例如,您可以首先发送消息长度或定义指示消息结尾的内容(例如\0\r\n)。

对不起,我对错误的描述不完整。在你的评论中,你说你增加了HOP消息大小!但是这当然不是一个好习惯,而且增加的大小非常小,从不会迫使操作系统立即发送它(实际上并没有强制操作系统这样做的特定大小)。如果您希望操作系统立即发送数据,则应使用TCP_NO_DELAY选项禁用Nagle算法,但在执行此操作之前,请查看How do I use TCP_NODELAY?这样做并不是一个好的做法,除此之外,在执行此操作时会导致数据包立即发送,因为您呼叫send,但它绝不会强制接收端的操作系统单独接收消息!那么这样做的正确方法是什么?

我详细解释这个问题:

// I don't know exact value of MAXDATASIZE but I will assume it is 128 
char buf[ MAXDATASIZE ]; 

int numbytes = recv(sock, buf, MAXDATASIZE, 0); 
if(numbyte == -1) { 
    // Handle error 
} 

// I assume HOP_MSG is a defined constant that contain value of HOP message 
if(strcmp(buf, HOP_MSG) == 0) { // <-- (1) 
    while((numbytes = recv(sock, buf, MAXDATASIZE, 0)) != -1) { // <-- (2) 
     if(numbytes == 0) break; 
    } 
    if(numbytes == -1) { 
     // Handle error 
    } 
} 

别急!符合(1)我认为recv完全读HOP_MSG,只有HOP_MSG,但为什么?正如我之前所说的TCP是一个流协议,它没有消息边界,所以它只能读取2个字节!或者阅读1KB(那肯定是超过HOP_MSG,所以我应该怎么办?

工作的答案是一样的东西如下:

int receive_till_zero(SOCKET sock, char* tmpbuf, int& numbytes) { 
    int i = 0; 
    do { 
     // Check if we have a complete message 
     for(; i < numbytes; i++) { 
      if(buf[i] == '\0') { 
       // \0 indicate end of message! so we are done 
       return i + 1; // return length of message 
      } 
     } 
     int n = recv(sock, buf + numbytes, MAXDATASIZE - numbytes, 0); 
     if(n == -1) { 
      return -1; // operation failed! 
     } 
     numbytes += n; 
    } while(true); 
} 
void remove_message_from_buffer(char* buf, int& numbytes, int msglen) { 
    // remove complete message from the buffer. 
    memmove(buf, buf + msglen, numbytes - msglen); 
    numbytes -= msglen; 
} 

void main() { 
    SOCKET s; 
    char buf[ MAXDATASIZE ]; 
    int numbytes = 0, msglen; 
    // Initialize socket and connect to server, you already do that 

    while(true) { 
     msglen = receive_till_zero(s, buf, numbytes); 
     if(msglen == -1) {/* Handle error */} 

     if(!strcmp(buf, HOP_MSG)) { 
      remove_message_from_buffer(buf, numbytes, msglen); 
      msglen = receive_till_zero(s, buf, numbytes); 
      if(msglen == -1) {/* Handle error */} 

      std::cout << "Message received from server: " << buf << std::endl; 
      remove_message_from_buffer(buf, numbytes, msglen); 
     } 
    } 
} 

通过调试这个代码,你一定会明白它的目的,receive_till_zero假设之前调用到recv的缓冲区中已经有一些待处理的数据,所以它将首先检查缓冲区中是否有完整的消息,并且从来没有假设只需要对recv进行一次调用即可完成接收数据,因此它会调用recv在一个循环中,直到它在缓冲区中看到一个\0。在我们f在我们称之为remove_message_from_buffer的缓冲区中的数据吞噬了数据并且仅仅那些数据,而不是从缓冲区的开始处开始接收,因为它们可能已经在缓冲区中存在一些数据。

正如你看到的代码是有点复杂,为了更好的编程模型和更好的C++代码,你可以使用boost::asio有一个很好的设计,并使用C完美地工作++和iostream

+0

我玩过更多。 A使消息更大,增加了一个\ 0,问题不在于它一次获得两个消息。它循环回来并等待接收,但是没有任何东西能够通过>。<“HOP Packet Next \ 0”,管理在其他情况下到达HOPREAD if(strcmp(“HOP Packet Next”,buf)== 0){ \t \t \t \t cout <<“HOP READ”<< endl; \t \t \t \t numbytes = 0; \t \t \t \t而(的numBytes == 0){ \t \t \t \t \t如果((的numBytes =的recv(的sockfd,BUF,MAXDATASIZE,0))== - 1){ \t \t \t \t \t \t PERROR( “recvROUTERThread0”); \t \t \t \t \t \t exit(1); \t \t \t \t \t} – L4nce0

+0

请看到我的编辑答案 – BigBoss

+0

我结束了刚刚发送的所有邮件一次,和黑客其余的工作。尽管您的更新非常有用,但下次我正在进行网络编程时,它会对我有所帮助。 – L4nce0

相关问题