我正在研究过去几天的套接字(在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丢包的数据包。)
请建议一种通过套接字快速发送和接收不同长度数据包的方法。
答案 0 :(得分:1)
我看到一些主要问题:
我认为您的代码忽略了send
函数中完整缓冲区的可能性。
在我看来,你的代码忽略了(没关系,我刚刚看到了新评论)recv
收集部分数据的可能性。
换句话说,您需要管理send
的用户范围缓冲区并处理recv
中的碎片。
代码使用的sizeof(int)
在不同的计算机上可能有不同的长度(可能使用uint32_t
代替?)。
代码不会转换为网络字节顺序。这意味着你要发送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
问题,我会将int
替换为特定的字节长度整数类型,例如int32_t
。
更多细节
请注意,单独发送整数并不保证单独收到整数或整数本身不会被分割。
send
函数写入系统的套接字缓冲区,不到网络(就像recv
从可用缓冲区读取而不是从线路读取)。
您无法控制碎片发生的位置或TCP数据包的打包方式(除非您实现自己的TCP / IP堆栈)。
我确信您很清楚“垃圾”值是服务器发送的数据。这意味着代码不是读取您发送的整数,而是读取另一段数据。
这可能是由于不完整的read
或不完整的send
导致的邮件边界对齐问题。
P.S。
我会考虑在TCP / IP层之上使用Websocket协议。
这保证了二进制数据包头,它可以使用不同的CPU架构(字节序),并提供更广泛的客户端连接(例如与浏览器连接等)。
它还将解决您遇到的数据包对齐问题(不是因为它不存在,而是因为它在您将采用的任何Websocket解析器中得到解决)。