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

时间:2017-09-25 11:35:11

标签: c sockets

我正在研究过去几天的套接字(在C中,没有套接字编程的经验)。 实际上我必须在覆盆子pi上收集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);

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

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

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

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

1 个答案:

答案 0 :(得分:1)

我看到一些主要问题:

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

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

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

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

  3. 代码不会转换为网络字节顺序。这意味着你要发送int的内存结构而不是可以被不同机器读取的整数(一些机器向后存储字节,一些向前存储,一些混合和匹配)。

  4. 请注意,当您使用TCP / IP发送更大的数据时,它将被分段为更小的数据包。

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

    要处理这些情况,您应该使用公平的网络设计而不是阻止套接字。

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

    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堆栈)。

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

    这可能是由于不完整的read或不完整的send导致的邮件边界对齐问题。

    P.S。

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

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

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