2017-09-25 18 views
0

过去几天我正在使用套接字(在C中,没有套接字编程的经验)。 其实我必须收集树莓派上的WiFi数据包,做一些处理,并且必须通过套接字(这两个设备都连接到网络中)将格式化的信息发送到另一个设备。以C的速度在C中的套接字上接收不同长度的数据包的连续流?

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

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

在发送端(客户端):

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的数据包)。

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

+2

什么是'actual_data'?它是*所有*充满数据?有没有部分填充的缓冲区?因为,你知道,TCP是一个*流*协议,没有固定大小的数据包或消息边界。这意味着你可能并不总是通过单一的“recv”调用发送所有这些内容,你必须循环以确保你能收到所有的数据。 –

+1

此外,由于您正在发送数据长度的“int”值(我假设),您确定没有任何[* endianness *](https://en.wikipedia.org/wiki/Endianness)问题是什么?由于在典型的ARM平台(如R-PI)和典型的x86 PC平台(如大多数现代台式计算机)上的排序通常不相同。 –

+0

@Someprogrammerdude'实际数据'就像一个英文文本(WiFi数据包解析和格式化) 是的......我知道它可能不会收到一次电话,因为我添加了一张支票,如果没有收到一个电话,我已经添加了代码来获取剩余的数据字节。 实际的问题是与字节的数量,它有时收到一些垃圾值 –

回答

1

我看到的几个主要问题:

  1. 我觉得你的代码忽略了send功能全缓冲的可能性。

    在我看来,您的代码忽略了由 recv收集部分数据的可能性。 (没关系,我只是在电视上看到的新评论)

    换句话说,你需要管理用户的土地缓冲sendrecv处理碎片。

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

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

请注意,当您使用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问题,我会与特定的字节长度整数类型更换intint32_t

的详细原因

请注意,发送整数分别不确实,这将是单独接收或整数本身不会被分割担保。

send功能写入到系统的缓冲区插座,到网络上(就像recv从可用的缓冲区中读取数据,而不是从线)。

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

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

这可能是与消息边界对齐的问题,由不完整的read或不完整的send引起。

P.S.

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

这保证了一个二进制数据包头,可以与不同的CPU架构(字节顺序)一起工作,并提供更多种类的客户端连接(例如连接浏览器等)。它也将解决你遇到的数据包对齐问题(不是因为它不存在,而是因为它解决了你将采用的任何Websocket解析器)。

+0

正如我上面提到的,这不是完整的代码。 但我可以通过检查以前收到的预期数据和实际接收到的数据字节来获取数据包的完整发送数据。 但是有时候,预期的数据长度(作为**单独的整数**发送)被作为垃圾值接收到 –

+0

@infiniteloop我编辑了我的答案,添加了一些代码审查细节,并在最后添加了一个简短的解释关于评论。 – Myst

相关问题