bra*_*ter 7 c++ sockets networking network-programming tcpsocket
我在通过(TCP)套接字传输数据时遇到了一个小问题.关于我在做什么的小背景:
我将数据从A侧发送到B.发送的数据可以是可变长度,假设最大大小为1096字节.
A) send(clientFd, buffer, size, NULL)
Run Code Online (Sandbox Code Playgroud)
在B上,因为我不知道期望的大小,我总是尝试接收1096字节:
B) int receivedBytes = receive(fd, msgBuff, 1096, NULL)
Run Code Online (Sandbox Code Playgroud)
但是,当我这样做时:我意识到A正在发送小块数据......大约80-90字节.经过几次发送后,B将他们聚集在一起,收到的是1296.这显然已经破坏了数据,地狱崩溃了.
为了解决这个问题,我将数据分为两部分:标题和数据.
struct IpcMsg
{
long msgType;
int devId;
uint32_t senderId;
uint16_t size;
uint8_t value[IPC_VALUES_SIZE];
};
Run Code Online (Sandbox Code Playgroud)
在一边:
A) send(clientFd, buffer, size, NULL)
Run Code Online (Sandbox Code Playgroud)
在B上,我首先收到标头并确定要接收的有效负载的大小:然后接收剩余的有效负载.
B) int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof( ((IpcMsg*)0)->value ), 0);
int sizeToPoll = ((IpcMsg*)buffer)->size;
printf("Size to poll: %d\n", sizeToPoll);
if (sizeToPoll != 0)
{
bytesRead = recv(clientFd, buffer + receivedBytes, sizeToPoll, 0);
}
Run Code Online (Sandbox Code Playgroud)
因此,对于每个有有效载荷的发送,我最终都会调用两次接收.这对我有用,但我想知道是否有更好的方法来做到这一点?
您的想法是发送包含有关以下数据的基本信息的标题,然后是数据本身.但是,这并不总是有效:
int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof( ((IpcMsg*)0)->value ), 0);
int sizeToPoll = ((IpcMsg*)buffer)->size;
Run Code Online (Sandbox Code Playgroud)
原因是TCP可以自由地分段,并根据自己对所谓的拥塞控制策略所应用的基础网络条件的评估,在其认为合适的块中发送您的报头.在局域网上,你几乎总是将你的标题放在一个数据包中,但是通过互联网在全世界范围内尝试它,你可以一次获得更少的字节数.
答案是不要直接调用TCP的'receive'(通常recv),而是将它抽象成一个小的实用函数,它接受你真正必须接收的大小和一个缓冲区来放入它.进入循环接收和附加数据包,直到所有数据到达或发生错误.
如果您需要同步并同时为多个客户端提供服务,则应用相同的主体,但您需要调查"select"调用,以便在数据到达时通知您.