目次
1.TCPスティッキーパケットの複製
ソケット通信によるデータの送受信は関係ありません。read()関数は、データが何度送信されても、できるだけ多くのデータを受信します。つまり、read()とwrite()の実行回数が異なる場合があります。
たとえば、write()は文字「abc」が送信されるたびに3回繰り返し実行され、ターゲットマシンのread()は3回受信し、毎回「abc」を受信する場合があります。また、2回受信する場合もあります。 1回目「abcab」と「cabc」を2回目に受信します。文字列「abcabcabc」を1回受信することもできます。
クライアントに一度に1人の学生の学生IDを送信させ、サーバーに学生の名前、住所、成績などの情報を返してもらいたいとします。このとき、問題が発生し、サーバーは学生IDを識別できません。たとえば、最初に1を送信し、2回目に3を送信する場合、サーバーはそれを13として処理する可能性があり、返された情報は明らかに間違っています。
これはデータの「スティッキーパケット」の問題です。クライアントから送信された複数のデータパケットは1つのデータパケットとして受信されます。これはデータのボーダレスとも呼ばれます。read()関数は、データの開始フラグと終了フラグを認識しません。データパケット(実際には、開始マークまたは終了マークもありません)であり、それらを連続データストリームとしてのみ扱います。
次のコードは、スティッキーパケットの問題を示しています。クライアントが99の長さのデータをサーバーに送信するたびに、サーバーは一度にまとめられた多数のデータを受信する場合があります。
サーバ:
/*================================================================
* Copyright (C) 2021 baichao All rights reserved.
*
* 文件名称:service1.c
* 创 建 者:baichao
* 创建日期:2021年01月25日
* 描 述:
*
================================================================*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(11230); //端口
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, SOMAXCONN);
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
int count = 0;
while(1)
{
//sleep(5);
char str[102400];
int readNum = read(clnt_sock,str,sizeof(str));
if(readNum <= 0)
continue;
std::cout<<"第"<<++count<<"次读取数据长度为:"<<readNum<<std::endl;
// write(clnt_sock, , sizeof(str));
}
close(clnt_sock);
close(serv_sock);
return 0;
}
クライアント:
/*================================================================
* Copyright (C) 2021 baichao All rights reserved.
*
* 文件名称:client1.cpp
* 创 建 者:baichao
* 创建日期:2021年01月25日
* 描 述:
*
================================================================*/
#include<iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(){
int serv_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(11230);
connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
char context[100] = {0};
for(int i = 0; i < sizeof(context)/sizeof(context[0]); ++i)
context[i] = '1';
while(1)
{
write(serv_sock,context,sizeof(context)-1);
}
close(serv_sock);
return 0;
}
演算結果:
データが読み取られてから48回目以降、スティッキーパケットの問題が発生していることがわかります。
2.解決策
2.1。サーバーは最初にデータの長さを送信し、次にデータを送信します
2.2。「\ n」などの各データの後に終了文字を設定します