2017-09-07 1 views
0

epoll을 사용하여 모든 클라이언트 이벤트를 모니터하는 TCP 소켓 통신을 구현합니다. 하나의 스레드 만 for 루프의 모든 클라이언트를 처리합니다. 모든 소켓은 비 블로킹입니다.tcp 소켓 recv는 recv buf가 이미 데이터를 가지고있을 때 "리소스를 일시적으로 사용할 수 없음"을 리턴합니다.

지금은 클라이언트가 MTU 이상으로 데이터를 보내면 여러 조각 패킷을 의미하므로 서버는 항상 모든 데이터를 완전히 읽을 수 없습니다. 아래처럼, 나는 머리를 먼저 읽고 머리에서 pdu len을 얻은 다음 pdu 부분을 읽는다.

문제는 내가 머리를 성공적으로 pdu recv()와 밀접하게 읽었지 만 항상 EAGAIN을 여러 번 반환합니다. 재 시도가 중단됩니다. 서버는 수천 개의 클라이언트 이벤트를 처리해야하기 때문에 재 시도를 항상 계속할 수있는 훌륭한 성능을 소비한다고 생각합니다.

모든 패킷이 최대 1448 바이트의 데이터 조각인데 tcpdump를 사용하지만 헤드가 5 바이트 밖에 없으므로 헤드를 읽을 수있는 이유는 무엇입니까? 그러나 다음 데이터 recv() 작업은 EAGAIN을 반환합니까? recv 버퍼에 데이터가 이미 도착했을 때 EAGAIN을 반환 할 수 있습니까? 필자는 처음 5 바이트를 읽을 수 있으므로 recv 버퍼에서 읽을 데이터가 더 있어야합니다.

은 tcp/ip 스택의 어셈블 과정과 관련이 있습니다. 코드는 아래와 같습니다. 모든 pdu recv는 10 회 이상 재 시도해야합니다. recv 할 수 있다면

... 
#define HDR_LEN 5 
n = epoll(epfd, events, 1000, -1) 
for(i =0; i < n; i++) 
{ 
    uint8 pHdr[HDR_LEN] = {0}; 
    uint16 pdulen = 0, offset =0; 
    infd = events[i].fd; 
    nRead = recv(infd, pHdr, HDR_LEN); // read the data head first 
    pdulen = ntohs(*(uint16 *)(pHdr+2)); // get the pdu len from the head 
    uint8 *pbuf = malloc(pdulen+HDR_LEN); 

    memcpy(pbuf, pHdr, HDR_LEN);   // move the head to buf 
    while(offset != pdulen)     // then read the pdu data 
    { 
     nRead = recv(infd, pbuf+HDR_LEN+offset, pdulen-offset); 
     if (nRead <=0) 
     { 
      if (nRead == -1 && errno == EAGAIN) // resource temporarily unavailable 
      { 
       if (retry < 5) 
       { 
        usleep(500); 
        retry++; 
        continue; 
       } 
       else 
        break; // already try 5 times, should always continue? 
      } 
      else 
       break; 
     } 
     else 
     { 
      offset += nRead; 
      retry = 0; 
     } 
    } 
    if (offset == pdulen) 
     process(pbuf, pdulen+HDR_LEN); // process the complete data 
    ... 
} 
... 
+0

이 문제를 해결하려면 도움을 청하십시오! – jackxie

+1

소켓이 비 차단 모드입니까? 그렇다면 수신 버퍼에 더 이상 데이터가 남아 있지 않을 때마다 EAGAIN이 예상됩니다. 다중 소켓의 비 차단 I/O를 동시에 처리하려면 부분 응답을받은 후 정상 poll() 호출로 돌아갈 수 있도록 상태 시스템을 구현해야합니다. 그런 다음 poll()이 소켓에서 읽을 데이터가 더 있음을 나타내면 이전에 소켓에서 가져온 데이터에 소켓의 추가 데이터를 다시 recv()해야합니다. 당신은 전체 PDU를 가지고 있습니다. –

+0

'recv buf에 이미 데이터가있는 경우 tcp socket recv는 "리소스를 일시적으로 사용할 수 없음"을 반환합니까? 아니. – EJP

답변

0

epoll 한 번, 차단하지 않고 당신을 말할 것이다. recv이 소켓에서 모든 데이터를 소비하면 다음 recv은 데이터가 추가 될 때까지 차단하거나 EAGAIN (비 차단 소켓 인 경우)을 반환합니다. 소켓을 읽을 수있을 때

  1. 사용 select/poll/epoll가 감지 :

    일반적인 패턴이다.

  2. 준비 소켓에서 recv을 한 번 호출하고 수신 된 데이터를 버퍼에 추가하십시오.
  3. 버퍼에 처리 할 데이터가 충분한 지 확인하십시오. 그렇다면 프로세스를 진행하십시오. 그렇지 않으면 select/poll/epoll에게 더 많은 것을 읽을 수있는시기를 알려주십시오.
관련 문제