Swoole TCP 스 트림 데이터 경계 문제 해결 방안

8816 단어 swooletcp
1.데이터 전송 과정
먼저 클 라 이언 트 가 데 이 터 를 버퍼 로 보 냅 니 다(서버 에서 직접 받 은 것 이 아 닙 니 다).클 라 이언 트 에 게 이번 데 이 터 는 전송 에 성 공 했 습 니 다.서버 에서 진정 으로 받 았 는 지 여 부 는 모 르 겠 습 니 다.그리고 서버 에서 버퍼 에서 데 이 터 를 읽 습 니 다.도해:

2.데이터 경계 란 무엇 인가
TCP 는 스 트림 전송 이기 때문에 서버 에 있어 서 버퍼 에 있 는 데이터 가 한 번 의 요청 인지 두 번 의 요청 인지 모 르 기 때문에 서버 에서 데 이 터 를 받 을 때 지정 한 문자 나 약속 한 길이 에 따라 데 이 터 를 나 누 어야 합 니 다.이 하 도 급 의 표 지 는 바로 데이터 경계 입 니 다.그렇지 않 으 면 한 번 에 두 개 이상 의 데 이 터 를 읽 거나 해석 하 는 오류 가 발생 할 수 있 습 니 다.

2.1 코드 데모
코드 로 구현 할 수 있 습 니 다.클 라 이언 트 가 버퍼 에'1'을 계속 입력 한다 고 가정 하면 매번 메시지 내용 이 1 인 것 과 같 습 니 다.그러면 서버 에서 읽 을 때 받 은 데 이 터 는 무 작위 길이 입 니 다.
클 라 이언 트 코드:

$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if ($client->connect('127.0.0.1', 9501, -1)) {
    while(true) {
        $client->send(1);        
    }
}
$client->close();
서버 코드:

$server = new Swoole\Server('127.0.0.1', 9501);
$server->on('connect', function($server, $fd){
    echo "client : ".$fd." connect";
});

$server->on('receive', function($server, $fd, $from_id, $data){
    echo "receive:". $data.PHP_EOL;
});

$server->on('close', function($server){

});
실행 결과

실행 결 과 를 볼 수 있 습 니 다.서버 에서 얻 은 데 이 터 는 완전히 무 작위 적 이 고 장단 점 이 있 습 니 다.그러면 다음 에 이 문 제 를 어떻게 해결 하 는 지 말씀 드 리 겠 습 니 다.
3.EOF 솔 루 션
첫 번 째 해결 방안 은 http 요청 헤더 의 구분자 와 유사 합 니 다.매번 보 내 는 패 킷 의 끝 에\r(설정 가능)를 사용 하여 끝 냅 니 다.서버 가 버퍼 에서 데 이 터 를 읽 을 때 지정 한 문자 에 따라 패 킷 을 분할 합 니 다.EOF 는 두 가지 설정 방안 이 있 습 니 다.
3.1 open_eof_check
우선 설정 방식 을 보 여 줍 니 다:

$server->set([
    'open_eof_check' => true,
    'package_eof' => "\r
" ]);
이러한 설정 방식 은 클 라 이언 트 가 보 낸 패 킷 을 검사 합 니 다.끝 이\r 인 것 을 발견 하면 워 커 프로 세 스,즉 우리 의 onReceive 리 셋 입 니 다.그렇지 않 으 면 버퍼 를 초과 하거나 시간 을 초과 할 때 까지 패 킷 을 계속 연결 합 니 다.그러나 이 방법 은 한 가지 문 제 는 여러 개의 패 킷 을 한꺼번에 받 을 수 있다 는 것 이다.그 는 패 킷 의 끝 부분 에서 검 사 를 했 기 때문에 데이터 내용 에\r 가 존재 할 때 프로그램 이 발견 하지 못 할 것 이다.우리 가 응용 코드 에서 다시\r 를 사용 하여 패 킷 을 나 누 어야 한다.
클 라 이언 트 실행 코드

$client = new Swoole\Client(SWOOLE_SOCK_TCP);

if ($client->connect('127.0.0.1', 9501, -1)) {

    while(true) {
        $send2 = "Hello World \r
"; $client->send($send2); } } $client->close();
서버 코드

$server = new Swoole\Server('127.0.0.1', 9501);
$server->set([
    'open_eof_check' => true,
    'package_eof' => "\r
" ]); $server->on('connect', function($server, $fd){ echo "client : ".$fd." connect"; }); $server->on('receive', function($server, $fd, $from_id, $data){ echo "receive:". $data; }); $server->on('close', function($server){ }); $server->start();
실행 결과

3.2 open_eof_split
설정 방법:

$server->set([
    'open_eof_split' => true,
    'package_eof' => "\r
" ]);
이러한 설정 방식 은 서버 에서 클 라 이언 트 가 보 낸 데 이 터 를 문자 별로 검사 합 니 다.\r 를 만나면 worker 프로 세 스 에 보 내 고 하 도 급 을 효과적으로 실현 할 수 있 지만 성능 이 떨 어 지 는 것 이 단점 입 니 다.
실행 결과:Hello World 를 받 을 때마다 볼 수 있 습 니 다.

3.3 open_eof_check 와 openeof_split 차이
open_eof_check 는 수신 데이터 의 끝 이 EOF 인지 만 검사 하기 때문에 성능 이 가장 좋 고 거의 소모 되 지 않 습 니 다.
open_eof_check 는 여러 개의 패 킷 합병 문 제 를 해결 할 수 없습니다.예 를 들 어 EOF 가 있 는 두 개의 데 이 터 를 동시에 보 내 면 바 텀 이 한 번 에 모두 돌아 올 수 있 습 니 다.
open_eof_split 는 왼쪽 에서 오른쪽으로 데 이 터 를 바이트 별로 비교 하고 데이터 중의 EOF 를 찾 아 패 킷 을 나 누 어 성능 이 떨 어 집 니 다.하지만 매번 패 킷 하나만 되 돌려 줍 니 다.
4.고정 헤드+패키지 솔 루 션
공식 문서 의 설명 참조:
패키지 길이 검 사 는 고정 패키지+패키지 라 는 형식 프로 토 콜 의 해석 을 제공 합 니 다.사용 하면 Worker 프로 세 스 onReceive 가 매번 완전한 패 킷 을 받 을 수 있 습 니 다.
길이 측정 프로 토 콜 은 한 번 의 길이 만 계산 하고 데이터 처 리 는 포인터 오프셋 만 진행 하 며 성능 이 매우 높 으 므 로 추천 합 니 다.
이 를 통 해 알 수 있 듯 이 정부 에서 이런 방식 을 추천 하 는 것 은 바로 배치 가 다른 방안 보다 복잡 하 다 는 것 이다.먼저 설정 을 붙 이 는 것 이다.

$server->set([
//         
'package_length_check' => true,
//               ,      10      。    pack()   
'package_length_type' => 'N',
// length             。
'package_length_offset' => 8,
//             ,    2    :
//length         (   +   ),package_body_offset   0
//      N   ,length        ,     ,package_body_offset     N
'package_body_offset' => 16,
//          ,     
'package_max_length' => 81920
]);
다음은 패 킷 구조의 예 로 필드 의 의 미 를 잘 나 타 낼 수 있다.

상기 통신 프로 토 콜 의 디자인 에서 포두 길 이 는 4 개의 정형 이 고 16 바이트 이 며 length 길 이 는 3 번 째 정형 에 있다.그래서 packagelength_offset 은 8,0-3 바이트 가 type,4-7 바이트 가 uid,8-11 바이트 가 length,12-15 바이트 가 serid 로 설정 되 어 있 습 니 다.
코드 구현 에 대해 말씀 드 리 겠 습 니 다.
클 라 이언 트 코드:

$client = new Swoole\Client(SWOOLE_SOCK_TCP);

$data = "123456789012345678901234567890";
$type = 0x30;
$uid = 0x123;
$length = strlen($data);
$serid = 0x15;
$head = pack("N4", $type, $uid, $length, $serid);
$body = pack("a{$length}", $data);
$message = $head.$body;


if ($client->connect('127.0.0.1', 9502, -1)) {
    $client->send($message);
    echo $client->recv();
}

$client->close();
서버 코드:

$serv = new Swoole\Server('127.0.0.1', 9502);
$serv->set([
    'open_length_check'     => true,
      'package_max_length'    => 81920,
      'package_length_type'   => 'N',
      'package_length_offset' => 8,
      'package_body_offset'   => 16,    
]);

$serv->on('connect', function($server, $fd){
    echo $fd. " Connect !".PHP_EOL;
});

$serv->on('receive', function($server, $fd, $from_id, $data){
    var_dump($data);            //    
    $tmp = unpack("Ntype/Nuid/Nlength", $data);
    $unpacking = unpack("Ntype/Nuid/Nlength/Nserid/a{$tmp['length']}body", $data);
    var_dump($unpacking);        //      
    $server->send($fd, " Server Receive Data: ". $unpacking['body']);
});


$serv->on('close', function($server){

});

$serv->start();
클 라 이언 트 실행 결과

서버 실행 결과

클 라 이언 트 가 보 낸 데 이 터 를 성공 적 으로 표시 하 는 것 을 볼 수 있 습 니 다.서버 에서 도 받 은 모든 데 이 터 를 출력 했 습 니 다.그 중에서 일부 필드 는 보 낼 때 16 진법 이기 때문에 서버 에서 받 은 후에 진법 으로 전환 해 야 합 니 다.저 는 여기 서 변환 을 하지 않 았 기 때문에 보 여 준 데 이 터 는 10 진법 입 니 다.
5.총화
비 교 를 통 해 알 수 있 듯 이 고정 포두+포 체 를 사용 하 는 방식 은 효율 이 가장 높 은 것 이다.왜냐하면 그 는 고정 길이 에 따라 읽 기 때문이다.그동안 pack 함수 의 사용 방법 을 알 아 봤 지만 이렇게 쓰 는 것 이 맞 는 지 는 확실 하지 않 습 니 다.다른 아 는 인형 이 있 으 면 아낌없이 대답 할 수 있 습 니 다.인터넷 관련 자료 가 적 고 공식 문서 에 도 몇 개의 필드 만 설명 되 어 있 습 니 다.
6.지식 확장
6.1 바이트 순서
컴퓨터 하드웨어 는 두 가지 데 이 터 를 저장 하 는 방식 이 있 습 니 다.큰 바이트 순서(big endian)와 작은 바이트 순서(little endian)입 니 다.
예 를 들 어 수치 0x 2211 은 두 개의 바이트 로 저장 합 니 다.높 은 바이트 는 0x 22 이 고 낮은 바이트 는 0x 11 입 니 다.
  • 큰 바이트 순서:높 은 바이트 가 앞 에 있 고 낮은 바이트 가 뒤에 있 습 니 다.이것 은 인류 가 수 치 를 읽 고 쓰 는 방법 입 니 다.
  • 작은 바이트 순서:낮은 바이트 가 앞 에 있 고 높 은 바이트 가 뒤에 있 으 면 0 x 1122 형식 으로 저장 합 니 다.
  • 이 앞 과 뒤 는 메모리 주 소 를 말 합 니 다.컴퓨터 가 바 이 트 를 처리 할 때 높 고 낮 음 바이트 의 구분 을 모 릅 니 다.바 이 트 를 순서대로 읽 을 줄 만 알 고 첫 번 째 바 이 트 를 먼저 읽 고 두 번 째 바 이 트 를 읽 습 니 다.
    예 를 들 어 0x 1234567 의 읽 기 순서:

    이상 은 Swoole TCP 스 트림 데이터 경계 문제 해결 방안 의 상세 한 내용 입 니 다.Swoole TCP 스 트림 데이터 경계 문제 해결 방안 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 십시오!

    좋은 웹페이지 즐겨찾기