诸暨网站制作哪些公司制作,天津科技网站,梅州市住房和城乡建设局官方网站,wordpress title通常情况下我们在编写套接字通信程序时都会实现一收一发的通信模式#xff0c;当客户端发送数据到服务端后#xff0c;我们希望服务端处理请求后同样返回给我们一个状态值#xff0c;并以此判断我们的请求是否被执行成功了#xff0c;另外增加收发同步有助于避免数据包粘包…通常情况下我们在编写套接字通信程序时都会实现一收一发的通信模式当客户端发送数据到服务端后我们希望服务端处理请求后同样返回给我们一个状态值并以此判断我们的请求是否被执行成功了另外增加收发同步有助于避免数据包粘包问题的产生在多数开发场景中我们都会实现该功能。
Socket粘包是指在使用TCP协议传输数据时发送方连续向接收方发送多个数据包时接收方可能会将它们合并成一个或多个大的数据包而不是按照发送方发送的原始数据包拆分成多个小的数据包进行接收。
造成粘包的原因主要有以下几个方面
TCP协议的特性TCP是一种面向连接的可靠传输协议保证了数据的正确性和可靠性。在TCP协议中发送方和接收方之间建立了一条虚拟的连接通过三次握手来建立连接。当数据在传输过程中出现丢失、损坏或延迟等问题时TCP会自动进行重传、校验等处理这些处理会导致接收方在接收数据时可能会一次性接收多个数据包。缓冲区的大小限制在接收方的缓冲区大小有限的情况下如果发送方发送的多个小数据包的总大小超过了接收方缓冲区的大小接收方可能会将它们合并成一个大的数据包来接收。数据的处理方式接收方在处理数据时可能会使用不同的方式来处理数据比如按照字节流方式读取数据或者按照固定长度读取数据等方式。不同的处理方式可能会导致接收方将多个数据包合并成一个大的数据包。
如果读者是一名Windows平台开发人员并从事过网络套接字开发那么一定很清楚此缺陷的产生当我们连续调用send()时就会产生粘包现象而解决此类方法的最好办法是在每次send()后调用一次recv()函数接收一个返回值至此由于数据包不连续则也就不会产生粘包的现象。
14.8.1 服务端实现
服务端我们实现的功能只有一个接收其中RecvFunction函数主要用于接收数据包通过使用recv函数接收来自socket连接通道的数据并根据接收到的数据判断条件决定是否发送数据回应。如果接收到的数据中命令参数满足command_int_a10和command_int_b20那么该函数会构建一个新的数据包将其发送回客户端其中包括一个表示成功执行的标志、一个包含欢迎信息的字符串以及其他数据信息。如果接收到的数据命令参数不满足上述条件则函数会构建一个新的数据包将其发送回客户端其中只包括一个表示执行失败的标志。最后函数返回一个BOOL类型的布尔值表示接收函数是否成功执行。
#include iostream
#include winsock2.h
#include WS2tcpip.h#pragma comment(lib,ws2_32.lib)typedef struct
{int command_int_a;int command_int_b;int command_int_c;int command_int_d;unsigned int command_uint_a;unsigned int command_uint_b;char command_string_a[256];char command_string_b[256];char command_string_c[256];char command_string_d[256];int flag;int count;
}send_recv_struct;// 调用接收函数
BOOL RecvFunction(SOCKET sock)
{// 接收数据char recv_buffer[8192] { 0 };int recv_flag recv(sock, (char *)recv_buffer, sizeof(send_recv_struct), 0);if (recv_flag 0){return FALSE;}send_recv_struct *buffer (send_recv_struct *)recv_buffer;std::cout 接收参数A: buffer-command_int_a std::endl;// 接收后判断,判断后发送标志或携带参数if (buffer-command_int_a 10 buffer-command_int_b 20){send_recv_struct send_buffer { 0 };send_buffer.flag 1;strcpy(send_buffer.command_string_a, hello lyshark);// 发送数据int send_flag send(sock, (char *)send_buffer, sizeof(send_recv_struct), 0);if (send_flag 0){return FALSE;}}else{send_recv_struct send_buffer { 0 };send_buffer.flag 0;// 发送数据int send_flag send(sock, (char *)send_buffer, sizeof(send_recv_struct), 0);if (send_flag 0){return FALSE;}return FALSE;}return TRUE;
}int main(int argc, char *argv[])
{WSADATA WSAData;if (WSAStartup(MAKEWORD(2, 0), WSAData) SOCKET_ERROR){std::cout WSA动态库初始化失败 std::endl;return 0;}SOCKET server_socket;if ((server_socket socket(AF_INET, SOCK_STREAM, 0)) ERROR){std::cout Socket 创建失败 std::endl;WSACleanup();return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family AF_INET;ServerAddr.sin_port htons(9999);ServerAddr.sin_addr.s_addr inet_addr(127.0.0.1);if (bind(server_socket, (LPSOCKADDR)ServerAddr, sizeof(ServerAddr)) SOCKET_ERROR){std::cout 绑定套接字失败 std::endl;closesocket(server_socket);WSACleanup();return 0;}if (listen(server_socket, 10) SOCKET_ERROR){std::cout 侦听套接字失败 std::endl;closesocket(server_socket);WSACleanup();return 0;}SOCKET message_socket;char buf[8192] { 0 };if ((message_socket accept(server_socket, (LPSOCKADDR)0, (int*)0)) INVALID_SOCKET){return 0;}send_recv_struct recv_buffer { 0 };// 接收对端数据到recv_bufferBOOL flag RecvFunction(message_socket);std::cout 接收状态: flag std::endl;closesocket(message_socket);closesocket(server_socket);WSACleanup();return 0;
}14.8.2 客户端实现
对于客户端而言其与服务端保持一致只需要封装一个对等的SendFunction函数该函数使用send函数将一个send_recv_struct类型的指针send_ptr发送到指定的socket连接通道。在发送完成后函数使用recv函数从socket连接通道接收数据并将其存储到一个char型数组recv_buffer中。接下来该函数使用send_recv_struct类型的指针buffer将该char型数组中的数据复制到一个新的send_recv_struct类型的结构体变量recv_ptr中最后返回一个BOOL类型的布尔值表示发送接收函数是否成功执行。
#include iostream
#include winsock2.h#pragma comment(lib,ws2_32.lib)typedef struct
{int command_int_a;int command_int_b;int command_int_c;int command_int_d;unsigned int command_uint_a;unsigned int command_uint_b;char command_string_a[256];char command_string_b[256];char command_string_c[256];char command_string_d[256];int flag;int count;
}send_recv_struct;// 调用发送接收函数
BOOL SendFunction(SOCKET sock, send_recv_struct send_ptr, send_recv_struct recv_ptr)
{// 发送数据int send_flag send(sock, (char *)send_ptr, sizeof(send_recv_struct), 0);if (send_flag 0){return FALSE;}// 接收数据char recv_buffer[8192] { 0 };int recv_flag recv(sock, (char *)recv_buffer, sizeof(send_recv_struct), 0);if (recv_flag 0){return FALSE;}send_recv_struct *buffer (send_recv_struct *)recv_buffer;memcpy((void *)recv_ptr, buffer, sizeof(send_recv_struct));return TRUE;
}int main(int argc, char* argv[])
{WSADATA WSAData;if (WSAStartup(MAKEWORD(2, 0), WSAData) SOCKET_ERROR){return 0;}SOCKET client_socket;if ((client_socket socket(AF_INET, SOCK_STREAM, 0)) SOCKET_ERROR){WSACleanup();return 0;}struct sockaddr_in ClientAddr;ClientAddr.sin_family AF_INET;ClientAddr.sin_port htons(9999);ClientAddr.sin_addr.s_addr inet_addr(127.0.0.1);if (connect(client_socket, (LPSOCKADDR)ClientAddr, sizeof(ClientAddr)) SOCKET_ERROR){closesocket(client_socket);WSACleanup();return 0;}send_recv_struct send_buffer {0};send_recv_struct response_buffer { 0 };// 填充发送数据包send_buffer.command_int_a 10;send_buffer.command_int_b 20;send_buffer.flag 0;// 发送数据包,并接收返回结果BOOL flag SendFunction(client_socket, send_buffer, response_buffer);if (flag FALSE){return 0;}std::cout 响应状态: response_buffer.flag std::endl;if (response_buffer.flag 1){std::cout 响应数据: response_buffer.command_string_a std::endl;}closesocket(client_socket);WSACleanup();return 0;
}运行上述代码片段读者可看到如下图所示的输出信息 本文作者 王瑞 本文链接 https://www.lyshark.com/post/4796bde3.html 版权声明 本博客所有文章除特别声明外均采用 BY-NC-SA 许可协议。转载请注明出处