c - 通过 C 语言的套接字快速接收不同长度数据包的连续流?

标签 c sockets

过去几天我正在研究套接字(用 C 语言,以前没有套接字编程经验)。 实际上我必须在树莓派上收集WiFi数据包,进行一些处理,并且必须通过套接字将格式化信息发送到另一个设备(两个设备都连接在网络中)。

我面临的挑战是通过套接字接收数据时。

发送数据时,发送端通过套接字发送成功,但接收端有时会收到一些垃圾或以前的数据。

在发送方(客户端):

int server_socket = socket(AF_INET, SOCK_STREAM, 0);
//connecting to the server with connect function
send(server_socket, &datalength, sizeof(datalength),0);  //datalength is an integer containing the number of bytes that are going to be sent next
send(server_socket, actual_data, sizeof(actual_data),0); //actual data is a char array containing the actual character string data

在接收端(服务器端):

int server_socket = socket(AF_INET, SOCK_STREAM, 0);
//bind the socket to the ip and port with bind function
//listen to the socket for any clients
//int client_socket = accept(server_socket, NULL, NULL);
int bytes;
recv(client_socket, &bytes, sizeof(bytes),0);
char* actual_message = malloc(bytes);
int rec_bytes = recv(client_socket, actual_message, bytes,0);

*上面的代码行不是实际的代码行,但流程和过程类似(带有异常处理和注释)。

有时,我可以快速获取所有数据包的实际数据(没有任何错误和数据包丢失)。但有时字节(发送的整数来告诉下一个事务的字节流的大小)被作为垃圾值接收,所以我的代码在那时就被破坏了。 有时,我在接收端收到的字节数小于预期的字节数(从接收到的整数字节数得知)。因此,在这种情况下,我会检查该条件并检索剩余的字节。

实际上,数据包到达的速率非常高(不到一秒大约1000 个数据包,我必须剖析、格式化并通过套接字发送)。我正在尝试不同的想法(使用 SOCK_DGRAMS 但这里有一些数据包丢失,在事务之间插入一些延迟,为每个数据包打开和关闭一个新套接字,在接收数据包后添加确认)但它们都不满足我的要求(快速传输0 丢包的数据包数)。

请建议一种通过套接字快速发送和接收不同长度数据包的方法。

最佳答案

我发现一些主要问题:

  1. 我认为您的代码忽略了 send 函数中缓冲区已满的可能性。

    在我看来,您的代码忽略了 recv 收集部分数据的可能性。(没关系,我刚刚看到了对此的新评论)

    换句话说,您需要管理send的用户态缓冲区并处理recv中的碎片。

  2. 代码使用 sizeof(int),在不同的机器上可能有不同的长度(也许使用 uint32_t 代替?)。

    <
  3. 代码不会在网络字节顺序之间进行转换。这意味着您发送的是 int 的内存结构,而不是可以由不同机器读取的整数(有些机器向后存储字节,有些机器向前存储字节,有些机器混合匹配)。

请注意,当您使用 TCP/IP 发送较大数据时,它将被分成较小的数据包。

这取决于 MTU 网络值(在野外通常以约 500 字节运行,在家庭网络中通常以约 1500 字节运行)。

要处理这些情况,您可能应该使用事件网络设计而不是阻塞套接字。

考虑通过与此类似的方式路由发送(如果您要使用阻塞套接字):

int send_complete(int fd, void * data, size_t len) {
    size_t act = 0;
    while(act < len) {
        int tmp = send(fd, (void *)((uintptr_t)data + act), len - act);
        if(tmp <= 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) 
            return tmp; // connection error
        act += tmp;
        // add `select` to poll the socket
    }
    return (int)act;
}

对于 sizeof 问题,我会将 int 替换为特定字节长度的整数类型,例如 int32_t

更多细节

请注意,单独发送整数并不能保证它会被单独接收或整数本身不会被分段。

send 函数写入套接字的系统缓冲区,写入网络(就像 recv 从可用缓冲区读取数据并不是来自电线)。

您无法控制碎片发生的位置或 TCP 数据包的打包方式(除非您实现自己的 TCP/IP 堆栈)。

我相信您很清楚“垃圾”值是服务器发送的数据。这意味着代码不是读取您发送的整数,而是读取另一条数据。

这可能是消息边界对齐的问题,由不完整的读取或不完整的发送引起。

附注

我会考虑在 TCP/IP 层之上使用 Websocket 协议(protocol)。

这保证了二进制数据包 header 可以与不同的 CPU 架构(字节顺序)配合使用,并提供更广泛的客户端连接(例如与浏览器连接等)。

它还将解决您遇到的数据包对齐问题(不是因为它不存在,而是因为它已在您将采用的任何 Websocket 解析器中得到解决)。

关于c - 通过 C 语言的套接字快速接收不同长度数据包的连续流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46404245/

相关文章:

c++ - Boost asio - 同步写入/读取 - 怎么做?

c++ - 循环套接字连接尝试最终会断开互联网连接

C - 一个程序可以为自己分配多少内存 - 它是如何确定的?

c - 将用户输入拆分为特定长度的字符串

c - ';' token 之前的错误 : expected ',' , ')' 或 '&'

java - 如何将 java.net.Socket 传递给等待 `BoostSocket.assign(tcp::v4(), nativeSocketFromJava);` 的 C++ DLL 函数

android - E/adbd : failed to connect to socket 'localabstract:com.example.talls.networkstest' : Connection refused

c - C中的套接字编程(客户端服务器示例)

objective-c - Objective c, Scanf() 字符串两次取同一个值

c - NULL 宏和 nul