根据《C中的Tcp/IP套接字》一书,我试图扩展一个使用Ansi C开发的Tcp客户端的示例。客户端连接到Tcp服务器,该服务器根据客户端提供的请求提供不同长度的字符串(我开发了自己的简单协议(。当返回的字符串长度较短时,一切正常。当它们超过一定长度时(例如4KB(,客户端会崩溃并出现"分段故障"错误。
套接字使用包装器来流式传输i/o:
FILE *str = fdopen(sock, "r+"); // Wrap for stream I/O
并且使用fwrite((和fread((来处理传输和接收。
这是在我的项目中生成错误的调用(调用方(:
uint8_t inbuf[MAX_WIRE_SIZE];
size_t respSize = GetNextMsg(str, inbuf, MAX_WIRE_SIZE); // Get the message
这是GetNextMsg((函数的实现,用于接收数据并对其进行解帧:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <netinet/in.h>
#include "Practical.h"
/* Read 4-byte length and place in big-endian order.
* Then read the indicated number of bytes.
* If the input buffer is too small for the data, truncate to fit and
* return the negation of the *indicated* length. Thus a negative return
* other than -1 indicates that the message was truncated.
* (Ambiguity is possible only if the caller passes an empty buffer.)
* Input stream is always left empty.
*/
uint32_t GetNextMsg(FILE *in, uint8_t *buf, size_t bufSize)
{
uint32_t mSize = 0;
uint32_t extra = 0;
if (fread(&mSize, sizeof(uint32_t), 1, in) != 1)
return -1;
mSize = ntohl(mSize);
if (mSize > bufSize)
{
extra = mSize - bufSize;
mSize = bufSize; // Truncate
}
if (fread(buf, sizeof(uint8_t), mSize, in) != mSize)
{
fprintf(stderr, "Framing error: expected %d, read lessn", mSize);
return -1;
}
if (extra > 0)
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
return -(mSize + extra); // Negation of indicated size
}
else
return mSize;
}
我怀疑这可能与以下事实有关,即使用Tcp,发送方和接收方使用流式行为处理数据,因此不允许接收方一次获取所有数据,这可能是我开始假设的简单示例。事实上,有了短字符串,一切都会正常工作。如果字符串较长,则不会。
我做了一个简单的调试,首先在函数内部插入一个printf,但当我崩溃时,它甚至不会打印出来。
当通过套接字接收比通常更长的消息。
缓冲区的大小远远大于导致问题的消息的长度(1MB与4KB(。我甚至试图通过setsockopt:来增加套接字缓冲区的大小
int rcvBufferSize;
// Retrieve and print the default buffer size
int sockOptSize = sizeof(rcvBufferSize);
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, (socklen_t*)&sockOptSize) < 0)
DieWithSystemMessage("getsockopt() failed");
printf("Initial Receive Buffer Size: %dn", rcvBufferSize);
// Double the buffer size
rcvBufferSize *= 10;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize,
sizeof(rcvBufferSize)) < 0)
DieWithSystemMessage("setsockopt() failed");
但这无济于事。
有什么关于原因的想法吗?我该怎么解决?
此代码:
{ // Message was truncated
uint32_t waste[BUFSIZE];
fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
将extra
字节读取到大小为4*BUFSIZE
的缓冲区(4,因为您打算将缓冲区设为unit8_t
,但意外地将其设为uint32_t
(。
如果extra
大于4*BUFSIZE
,则会出现本地缓冲区溢出和堆栈损坏,可能导致崩溃。
为了正确地做到这一点,需要这样的东西:
int remaining = extra;
while (remaining > 0) {
char waste[BUFSIZE];
int to_read = min(BUFSIZE, remaining);
int got = fread(waste, 1, to_read, in);
if (got <= 0) break;
remaining -= got;
}