如何修复 Ansi C Tcp 客户端的分段错误?
How to fix a segmentation fault for Ansi C Tcp client?
我正在尝试扩展一个使用 Ansi C 开发的 Tcp 客户端的示例,遵循本书 "TCP/IP Sockets in C"。客户端连接到提供不同长度字符串的 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 less\n", 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 作为函数内部的第一件事插入,但是当我发生崩溃时,它甚至没有被打印出来。
作为参数传递给函数的 FILE *str 似乎有问题,当
通过套接字收到比平时更长的消息。
缓冲区的大小远远大于导致问题的消息长度(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: %d\n", 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;
}
我正在尝试扩展一个使用 Ansi C 开发的 Tcp 客户端的示例,遵循本书 "TCP/IP Sockets in C"。客户端连接到提供不同长度字符串的 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 less\n", 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 作为函数内部的第一件事插入,但是当我发生崩溃时,它甚至没有被打印出来。
作为参数传递给函数的 FILE *str 似乎有问题,当 通过套接字收到比平时更长的消息。
缓冲区的大小远远大于导致问题的消息长度(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: %d\n", 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;
}