Swoole TCP 스 트림 데이터 경계 문제 해결 방안
먼저 클 라 이언 트 가 데 이 터 를 버퍼 로 보 냅 니 다(서버 에서 직접 받 은 것 이 아 닙 니 다).클 라 이언 트 에 게 이번 데 이 터 는 전송 에 성 공 했 습 니 다.서버 에서 진정 으로 받 았 는 지 여 부 는 모 르 겠 습 니 다.그리고 서버 에서 버퍼 에서 데 이 터 를 읽 습 니 다.도해:
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 입 니 다.
예 를 들 어 0x 1234567 의 읽 기 순서:
이상 은 Swoole TCP 스 트림 데이터 경계 문제 해결 방안 의 상세 한 내용 입 니 다.Swoole TCP 스 트림 데이터 경계 문제 해결 방안 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 십시오!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
tcp 서버 만들기/** * Created by PhpStorm. * User: Administrator * Date: 2018/4/3 * Time: 16:00 */ //创建服务器 TCP服务器 $serv = new swoole_ser...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.