2012-11-23 2 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()); 

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를 관리 작동합니다.

위의 관리자 코드와 관리자 코드 2는 모두이 코드 부분으로 전송됩니다. 첫 번째 전송을 수신합니다 (이 경우 "HOP"). HOP 패킷 파싱을 제거 했으므로, literalally 뭔가가 읽혀 져야한다고 명시해야합니다.

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으로 전화하면 첫 번째 통화에서 이미 수신 된 데이터는 차단됩니다. 또한 큰 버퍼를 보낼 때 recvsend을 사용하여 전달 된 단일 메시지보다 적은 데이터를 수신 할 수 있으므로 다른 문제 일 수 있습니다.

수신 된 스트림에서 메시지 끝을 정의하는 프로토콜을 정의한 다음 해당 프로토콜에 따라 데이터를 수신해야합니다. 예를 들어, 먼저 메시지의 길이를 보내거나 메시지의 끝을 나타내는 무언가를 정의 할 수 있습니다 (예 : \0 또는 \r\n).

불완전한 설명에 대해 죄송합니다. 귀하의 의견에 당신은 HOP 메시지 크기를 증가했다고 말합니다! 그러나 확실히 좋은 방법은 아닙니다. 또한 크기를 늘리면 OS가 강제로 절대로 강제로 보내지 않을 정도로 작습니다 (사실 OS가 강제하는 특정 크기가 없습니다). OS가 데이터를 즉시 보내도록하려면 TCP_NO_DELAY 옵션을 사용하여 Nagle 알고리즘을 비활성화해야하지만 그 전에는 How do I use TCP_NODELAY?을 살펴보십시오. 이렇게하는 것은 좋은 연습이 아니며 옆에 send으로 전화를 걸어 패킷이 즉시 전송되지만 수신자 측의 OS가 메시지를 개별적으로 수신하도록하지 않도록하십시오! 이렇게하려면 올바른 방법은 무엇입니까?

// 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)표시된 나는 HOP_MSG 읽기 및 HOP_MSG,하지만 왜? 전에 말했듯이 TCP은 스트림 프로토콜이며 메시지 경계가 없으므로 2 바이트 만 읽을 수 있습니다 !! , receive_till_zero

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); 
     } 
    } 
} 

이 코드를 디버깅함으로써 당신은 확실히 그것의 목적을 이해하는 것 또는 그 확실히 이상 HOP_MSG입니다 (1KB을 읽고, 그래서? 무엇을해야

실무 대답은 다음과 같은 것입니다 이전 호출에서 버퍼에 보류중인 데이터가 이미 있다고 가정하면 recv에 버퍼에 전체 메시지가 있는지 먼저 확인하고 recv에 한 번만 완료된 데이터 수신을 가정하지 않습니다. 그러면 호출됩니다. 버퍼에 \0이 나타날 때까지 루프에서 recv을 찾습니다 .f 버퍼에있는 데이터와 함께 우리는 remove_message_from_buffer이라고 부르며 그 데이터와 그 데이터만을 먹었을뿐 아니라 이미 버퍼의 일부 데이터가있을 수 있기 때문에 버퍼의 시작부터 수신을 시작하지 않습니다.

더 나은 프로그래밍 모델과는 아주 좋은 디자인을 가지고 boost::asio를 사용하여 C와 함께 완벽하게 작동 ++ 수 있고 더 나은 C++ 코드, 코드가 약간 복잡하다시피 iostream

+0

나는 그걸 가지고 놀았습니다. A가 메시지를 더 크게 만들었고 \ 0을 추가했습니다. 문제는 두 메시지를 동시에 가져 오는 것이 아닙니다. (strcmp ("HOP Packet Next", buf) == 0) { } "HOP 패킷 다음 \ 0", 다른 곳에서 HOPREAD로 이동하기 위해 관리합니다. \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

관련 문제