我将自己介绍给C / C ++中的套接字编程,并使用send()
和recv()
通过TCP
套接字在客户端和服务器程序之间交换数据。
以下是我的代码中的一些相关摘录:
server.c :
char recv_data[1024];
// Socket setup and so on ommited...
bytes_recieved = recv(connected, recv_data, 1024, 0);
recv_data[bytes_recieved] = '\0';
client.c:
char send_data[1024];
// Setup ommited...
send(connected, send_data, strlen(send_data), 0);
recv()
本身是否提供任何防止缓冲区溢出的保护?例如,如果我将第3个参数更改为recv()
高于recv_data
指向的缓冲区(例如4000) - 这是否会导致缓冲区溢出? (我实际上尝试过这样做,但似乎无法触发段错误。)
我实际上是在尝试创建一个故意易受攻击的服务器程序来更好地理解这些问题,这就是为什么我试图通过recv()
溢出。
修订:
不无关系,会发现为什么上面的client.c
发送的内容超过1024
指定的strlen(send_data)
字节。我正在使用gets(send_data)
从标准输入填充该缓冲区,但如果我通过标准输入输入超过1024个字节,server.c
程序会显示它收到所有字节! :)。 strlen(send_data)
的{{1}}是否不限制发送的字节数?
答案 0 :(得分:9)
例如,如果我将第三个参数更改为recv()更高于recv_data指向的缓冲区(例如4000) - 这是否会导致缓冲区溢出?
当然是的。如果网络缓冲区有4000字节的数据,它将把它放在缓冲区中。关键点在于,recv就像任何其他带有缓冲区的C API一样,它的长度相信调用者会传递缓冲区的实际长度,如果调用者传递的长度不正确,那么错误在于调用者,它可以导致未定义的行为。
在C中,当您将数组传递给函数时,被调用函数无法知道数组的大小。因此,所有API都只依赖于您提供的输入。
char recv_data[1024];
// Socket setup and so on ommited...
bytes_recieved = recv(connected, recv_data, 1024, 0);
recv_data[bytes_recieved] = '\0';
上述代码可能会以多种方式引发麻烦。在以下情况下会导致不确定的行为:
(a)如果recv返回-1
,那么你直接索引到recv_data缓冲区而不检查返回值
(b)如果recv返回1024
,则再次导致超出限制访问,因为应从1024
到0
访问大小为1023
的数组。
答案 1 :(得分:5)
这个
recv_data[bytes_recieved] = '\0';
如果收到1024字节,可能导致缓冲区溢出。
您可能想要更改此
bytes_recieved = recv(connected, recv_data, 1024, 0);
成为
bytes_recieved = recv(connected, recv_data, 1024 - 1, 0);
这样bytes_recieved
永远不会大于1023,这是recv_data
的最大有效索引。
您的系统调用(recv()
/ send()
)也没有错误检查。在以任何其他方式使用结果之前测试它们是否已返回-1
。
参考你的修正案:
strlen()
尝试返回从其参数指向的字符开始直到第一个NUL
/ 0
字符的字符数。此数字可以是任何值,具体取决于您放置终止0
的位置。
如果此0
- 终结符的搜索在分配给strlen()
参数的内存后面运行,程序肯定会遇到未定义的行为,因此可以返回任何值。
所以回答你的问题:如果send_data
不 0终止strlen()
会使应用程序遇到未定义的行为,因此可能会崩溃或strlen()
返回大于1024的值,因此send()
会尝试发送此数量的字符。
答案 2 :(得分:0)
即使你发送的字节数大于recv()
缓冲区,你仍然可以recv()
成功调用recv()
,这就是为什么你说bytes_received
是仍然是5000
个字节,因为,假设你发送5000
个字节,而你的接收缓冲区是1000
字节,在第一次调用recv()
时它只会得到1000
}}字节,在下一次调用时,再次1000
个字节,直到它收到所有数据。所以,我认为这里没有缓冲区溢出。这就是TCP的工作方式。