C\#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 모델 은 패 킷 부착 문 제 를 자동 으로 해결 해 준다.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebView2를 Visual Studio 2017 Express에서 사용할 수 있을 때까지Evergreen .Net Framework SDK 4.8 VisualStudio2017에서 NuGet을 사용하기 때문에 패키지 관리 방법을 packages.config 대신 PackageReference를 사용해야...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.