我想知道,如果我用 C 编写的客户端执行 4 次 send(),我的服务器是否需要 read() 4 次,还是第一次读取会一起读取所有 4 个 send()?
3 回答
为了帮助你一点,你基本上已经回答了你自己的问题。
可以这么说,您需要“分层”进行。
客户:
int len = strlen(Filename) + 1; //Mind the terminating 0.
send(sock, (const char *)&len, sizeof(int), 0);
send(sock, Filename, len, 0); //Sending the filename
send(sock, &FileSize, sizeof(int), 0);
send(sock, FileBuf, FileSize, 0);
此代码将在途中发送整个数据(假设整个文件在“FileBuf”-Variable 中)。
服务器:
int len;
char *FileBuf, FileName[20];
recv(sock, &len, sizeof(int), 0); //Receives the filename length. (4 Bytes)
recv(sock, FileName, len, 0); //Receives the filename (x bytes)
recv(sock, &len, sizeof(int), 0); //Receives the file length (again, 4 bytes)
FileBuf = new char[len]; //Creates sufficient space in memory.
recv(sock, FileBuf, len, 0); //Receives the file into the appropriate variable.
这是绝对的准系统变体,不是很可靠,但你应该明白。
更强大的方法需要您检查 recv() 和 send() 的返回值。两者都将返回已使用此调用处理的字节数。如果此数量等于“0”,则表示连接已被另一端关闭。(主要用于recv()
)。如果它等于 -1,则意味着出现问题,您应该检查errno
变量。
如果一切顺利,它等于您发送/尝试接收的确切字节数。
但是,如果它不是“len”(或 0 或 -1),您可以编写一个像这样的小包装器。
unsigned char Recv(int sock, void *target, int Len) {
unsigned char *ptr = (unsigned char *)target, Ret = 0;
int RecvBytes = 1;
while(Len && !Ret) {
RecvBytes = recv(sock, ptr, Len, 0);
if(!RecvBytes) Ret = 1;
else if(RecvBytes == -1) Ret = errno;
else {
Len -= RecvBytes;
ptr += RecvBytes;
}
}
return Ret;
}
这段代码的作用:它一直在接收,直到您收到您期望的所有数据(Len
参数)或发生错误。如果一切顺利,它会返回“0”,您可以使用if(!Recv())
.
另一个有用的包装函数(可以说是一种快捷方式)是这个:
uint32_t RecvInt(int sock) {
uint32_t Ret;
Recv(sock, &Ret, sizeof(Ret));
return ntohl(Ret);
}
该函数只接收一个无符号整数并将字节序从网络字节顺序更正为主机字节顺序。(网络字节序总是大端,主机字节序往往是:小端)
使用这些包装函数,可以像这样更改代码:
uint32_t len;
char *FileBuf, FileName[20];
len = RecvInt(sock); //Receives the filename length. (4 Bytes)
Recv(sock, FileName, len); //Receives the filename (x bytes)
len = RecvInt(sock); //Receives the file length (again, 4 bytes)
FileBuf = new char[len]; //Creates sufficient space in memory.
Recv(sock, FileBuf, len); //Receives the file into the appropriate variable.
对于流套接字(例如 TCP) :在发送端进行多少send()
或调用没有区别。write()
数据可以少至一个块,多至 n 个块,每块 1 个字节(其中 n 是发送的字节数),或介于两者之间的任何数据。
对于数据报套接字(例如 UDP):每个recv()
orrecvmsg()
调用将返回一个从另一端发送的完整数据报。recv()
or调用的数量recvmsg()
应该与发送的数据报的数量相同。recv()
从数据报套接字读取时是首选,但我相信read()
应该表现相同。
write()
s的数量read()
不必相同 - 有可能write()
将所有数据写入一个片段,但在另一台计算机上read()
只能设法以几个块的形式接收它,反之亦然。这就是为什么您应该始终检查这些函数的返回值,如果仅发生部分数据传输,则继续发送/接收其余部分。