C\#TCP 접착 문제 해결 방법

1.TCP 접착 이 발생 하 는 원리
1.TCP 패 키 지 는 발송 자가 보 낸 여러 패 키 지 를 수신 자 에 게 받 을 때 한 패 키 지 를 붙 이 는 것 을 말한다.수신 버퍼 에서 볼 때 뒤의 패 키 지 는 앞의 패 킷 데이터 의 끝 에 바짝 붙 는 것 을 말한다.접착 현상 이 발생 하 는 원인 은 여러 가지 로 발송 자 에 의 해 발생 할 수도 있 고 수신 자 에 의 해 발생 할 수도 있다.
2.발송 자가 야기 하 는 패 키 지 는 TCP 프로 토 콜 자체 에 의 해 이 루어 진 것 이다.TCP 는 전송 효율 을 높이 기 위해 발송 자 는 충분 한 데 이 터 를 수집 한 후에 야 데 이 터 를 보 내야 한다.만약 에 몇 번 연속 으로 보 내 는 데이터 가 매우 적 으 면 보통 TCP 는 최적화 알고리즘 에 따라 이 데 이 터 를 한 봉지 로 합성 한 후에 한 번 에 보 내 면 수신 자 는 패 킷 데 이 터 를 받 을 수 있다.수신 자가 일 으 키 는 패 키 지 는 수신 자 사용자 프로 세 스 가 데 이 터 를 제때에 받 지 않 아 패 키 지 를 붙 이 는 현상 을 초래 한 것 이다.
3.수신 자가 먼저 받 은 데 이 터 를 시스템 수신 버퍼 에 두 었 기 때 문 입 니 다.사용자 프로 세 스 는 이 버퍼 에서 데 이 터 를 가 져 옵 니 다.다음 패키지 데이터 가 도 착 했 을 때 이전 패키지 의 데 이 터 를 사용자 프로 세 스 가 가 져 가지 않 았 다 면 다음 패키지 의 데 이 터 를 시스템 수신 버퍼 에 넣 었 을 때 이전 패키지 의 수 거 를 받 은 후에사용자 프로 세 스 는 미리 설 정 된 버퍼 크기 에 따라 시스템 에서 버퍼 에서 데 이 터 를 가 져 와 한 번 에 여러 개의 패 킷 데 이 터 를 가 져 옵 니 다.
2.해결 원리 와 코드 실현
1.포두(고정 길이,안에 포 개체 의 길이,발송 시 동적 획득)+포 개체 의 전송 메커니즘 을 사용한다.그림 과 같다

HeaderSize 는 패키지 의 길 이 를 저장 하고 있 으 며 HeaderSize 자체 가 4 바이트 로 정 해 져 있 습 니 다.
완전한 패 킷(L)=HeaderSize+Body Size;
2.하 도 급 알고리즘
그 기본 적 인 사 고 는 먼저 처리 해 야 할 수신 데이터 흐름,즉 시스템 버퍼 데이터(길 이 를 M 으로 설정)를 예 정 된 구조 데이터 형식 으로 강제로 전환 하고 그 중에서 구조 데이터 길이 필드 L 을 꺼 낸 다음 에 포두 에 따라 첫 번 째 패 킷 데이터 길 이 를 계산 하 는 것 이다.
       M=시스템 버퍼 크기;L=사용자 가 보 낸 패 킷=HeaderSize+Body Size;
1)L
2)L=M 이면 데이터 흐름 내용 이 완전한 구조 데이터 임 을 나타 낸다(즉,사용자 정의 버퍼 는 시스템 수신 버퍼 크기 와 같다).임시 버퍼 에 직접 저장 하면 된다.

3)L>M 이면 데이터 흐름 내용 이 완전한 구조 데 이 터 를 구성 하지 못 한 다 는 것 을 나타 내 므 로 다음 패키지 데이터 와 합 친 후에 처리 해 야 한다.

4)다음은 코드 구현(HP-SOCKET 프레임 워 크 의 서버 측 에서 데 이 터 를 수신)

int headSize = 4;//       4
  byte[] surplusBuffer = null;//       ,         
  /// <summary>
  ///           
  /// </summary>
  /// <param name="connId">       ID</param>
  /// <param name="bytes">     </param>
  /// <returns></returns>
  private HandleResult OnReceive(IntPtr connId, byte[] bytes) 
  {
   //bytes         
   //bytesRead        
   int bytesRead = bytes.Length;
   if (bytesRead > 0)
   {
    if (surplusBuffer == null)//          ,       
     surplusBuffer = bytes;//                  
    else
     surplusBuffer = surplusBuffer.Concat(bytes).ToArray();//         
    //             
    int haveRead = 0;
    //  totalLen              (      surplusBuffer       +       )
    int totalLen = surplusBuffer.Length;
    while (haveRead <= totalLen)
    {
     //   N                     
     //       N       ,              
     if (totalLen - haveRead < headSize)
     {
      byte[] byteSub = new byte[totalLen - haveRead];
      //                
      Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
      surplusBuffer = byteSub;
      totalLen = 0;
      break;
     }
     //         ,        
     byte[] headByte = new byte[headSize];
     Buffer.BlockCopy(surplusBuffer, haveRead, headByte, 0, headSize);//            
     int bodySize = BitConverter.ToInt32(headByte, 0);//             

     //    haveRead=  N         0  ;0,1,2,3....N
     //          N             ,                ,     
     if (haveRead + headSize + bodySize > totalLen)
     {
      byte[] byteSub = new byte[totalLen - haveRead];
      Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
      surplusBuffer = byteSub;
      break;
     }
     else
     {
      //       ,       
      String strc = Encoding.UTF8.GetString(surplusBuffer, haveRead + headSize, bodySize);
      //AddMsg(string.Format(" > [OnReceive] -> {0}", strc));
      //             
      haveRead = haveRead + headSize + bodySize;
      if (headSize + bodySize == bytesRead)//                     ,             0
      {
       surplusBuffer = null;//          
       totalLen = 0;// 0
      }
     }
    }
   }
   return HandleResult.Ok;
  }

가방 을 뜯 고 문 자 를 해석 하 는 작업 을 마 칠 때 가 되 었 습 니 다.하지만 아직 완성 되 지 않 았 습 니 다.이 코드 가 클 라 이언 트 가 서버 에서 온 데 이 터 를 받 는 것 이 라면 괜 찮 습 니 다.
IntPtr connId 의 연 결 된 세 션 ID 를 자세히 보십시오.
private HandleResult OnReceive(IntPtr connId, byte[] bytes)
{
}
그러나 서버 쪽 에 서 는 모든 패 킷 이 어떤 세 션 에서 생 겨 났 는 지 구분 해 야 합 니 다.서버 쪽 은 다 중 스 레 드,다 중 사용자 모드 이기 때문에 첫 번 째 패 킷 과 두 번 째 는 서로 다른 세 션 에서 나 올 수 있 기 때문에 위의 코드 는 단일 세 션 모드 에 만 적 용 됩 니 다.
다음은 내 가 이 문 제 를 해결 할 것 이다.
c\#안전 한 ConcurrentDictionary 를 사용 하여 구체 적 으로 참고 하 십시오https://msdn.microsoft.com/zh-cn/library/dd287191(v=vs.110).aspx
최신 코드

//       
  ConcurrentDictionary<IntPtr, byte[]> dic = new ConcurrentDictionary<IntPtr, byte[]>();
  int headSize = 4;//       4
  /// <summary>
  ///           
  /// </summary>
  /// <param name="connId">       ID</param>
  /// <param name="bytes">     </param>
  /// <returns></returns>
  private HandleResult OnReceive(IntPtr connId, byte[] bytes) 
  {
   //bytes         
   //bytesRead        
   int bytesRead = bytes.Length;
   if (bytesRead > 0)
   {
    byte[] surplusBuffer = null;
    if (dic.TryGetValue(connId, out surplusBuffer))
    {
     byte[] curBuffer = surplusBuffer.Concat(bytes).ToArray();//         
     //    ID      
     dic.TryUpdate(connId, curBuffer, surplusBuffer);
     surplusBuffer = curBuffer;//  
    }
    else
    {
     //    ID bytes
     dic.TryAdd(connId, bytes);
     surplusBuffer = bytes;//  
    }

    //             
    int haveRead = 0;
    //  totalLen              (      surplusBuffer       +       )
    int totalLen = surplusBuffer.Length;
    while (haveRead <= totalLen)
    {
     //   N                     
     //       N       ,              
     if (totalLen - haveRead < headSize)
     {
      byte[] byteSub = new byte[totalLen - haveRead];
      //                
      Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
      dic.TryUpdate(connId, byteSub, surplusBuffer);
      surplusBuffer = byteSub;
      totalLen = 0;
      break;
     }
     //         ,        
     byte[] headByte = new byte[headSize];
     Buffer.BlockCopy(surplusBuffer, haveRead, headByte, 0, headSize);//            
     int bodySize = BitConverter.ToInt32(headByte, 0);//             

     //    haveRead=  N         0  ;0,1,2,3....N
     //          N             ,                ,     
     if (haveRead + headSize + bodySize > totalLen)
     {
      byte[] byteSub = new byte[totalLen - haveRead];
      Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
      dic.TryUpdate(connId, byteSub, surplusBuffer);
      surplusBuffer = byteSub;
      break;
     }
     else
     {
      //       ,       
      String strc = Encoding.UTF8.GetString(surplusBuffer, haveRead + headSize, bodySize);
      AddMsg(string.Format(" > {0}[OnReceive] -> {1}", connId, strc));
      //             
      haveRead = haveRead + headSize + bodySize;
      if (headSize + bodySize == bytesRead)//                     ,             0
      {
       byte[] xbtye=null;
       dic.TryRemove(connId, out xbtye);
       surplusBuffer = null;//          
       totalLen = 0;// 0
      }
     }
    }
   }
   return HandleResult.Ok;
  }

이렇게 하면 다 중 클 라 이언 트 세 션 으로 인 한 수신 혼란 이 해 결 됩 니 다.이로써 모든 일이 끝났다.이상 의 코드 는 학습 을 참고 하기 위해 서 입 니 다.만약 정말 이렇게 귀 찮 게 하고 싶 지 않다 면.HP-SOCKET 통신 프레임 워 크 를 직접 사용 할 수 있 는 PACK 모델 은 패 킷 부착 문 제 를 자동 으로 해결 해 준다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기