阅读背景:

3.Linux网络编程-粘包处理

来源:互联网 
/* * visiopacket.h * * Created on: 2019年5月8日 * Author: hfeng.liu * * 粘包处理:由于TCP是基于流传输的机制,当发送多个间隔较小的小报文时,它会在缓冲区中缓存成一个报文发送给peer,这时peer * 无法区分一个报文的边界。 * 解决的办法:让peer知道一个报文的边界,即知道它的长度。 */ #include <string> #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <errno.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while(0) #define handle_success(msg) \ do { printf(msg); exit(EXIT_SUCCESS); } while(0) typedef struct message { uint32_t length; char buf[1024]; }msg; //readn函数 //说明:此函数解决了粘包的数据处理; //@ssize_t:-1表示返回错误,0表示收到FIN信号,>0表示数据的长度 //@fd:文件描述符 //@buf:待写数据首地址 //@nByte:待读长度 ssize_t readn(int fd, void *buf, size_t nByte) { size_t nleft = nByte; ssize_t nread = 0; char * buf_p = (char*)buf; while (nleft > 0) { nread = read(fd, buf_p, nleft); if (nread<0 && errno==EINTR) continue; else if (nread == 0) return 0; else if (nread < 0) return -1; else return nread; nleft -= nread; buf_p += nread; } return nByte; } //writen函数 //说明:此函数解决了缓冲区数据发送溢出的处理; //@ssize_t:-1表示返回错误,0表示收到FIN信号,>0表示数据的长度 //@fd:文件描述符 //@buf:待写数据首地址 //@nByte:待写长度 ssize_t writen(int fd, void *buf, size_t nBytes) { size_t nleft = nBytes; char *buf_p = (char*)buf; int nwritten = 0; while(nleft > 0) { nwritten = write(fd, buf_p, nleft); if (nwritten<0 && errno==EINTR) nwritten = 0; else return -1; nleft -= nwritten; buf_p += nwritten; } return nBytes; } //回射服务器客户端 #include <string> #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <errno.h> #include "visiopacket.h" #define PORT 50001 #define ADDR ("127.0.0.1") int main() { sockaddr_in s_sock, c_sock; socklen_t sockLen = sizeof(sockaddr); s_sock.sin_family = AF_INET; s_sock.sin_port = htons(PORT); inet_aton(ADDR, &s_sock.sin_addr); //socket int sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd < 0) handle_error("socket()"); //connect if (connect(sfd, (sockaddr*)&s_sock, sizeof(sockaddr)) < 0) handle_error("connect()"); if (getpeername(sfd, (sockaddr*)&c_sock, &sockLen) != 0) handle_error("getpeername()") ; printf("peer sock info: %s:%d!\n", inet_ntoa(c_sock.sin_addr), ntohs(c_sock.sin_port)); msg send_msg; msg rcv_msg; while(fgets(send_msg.buf, sizeof(send_msg.buf), stdin) != NULL) { send_msg.length = strlen(send_msg.buf); printf("length:%d\n", send_msg.length); writen(sfd, &send_msg, send_msg.length + sizeof(send_msg.length)); read(sfd, &rcv_msg.length, sizeof(rcv_msg.length)); int ret = readn(sfd, rcv_msg.buf, rcv_msg.length); if (ret < 0) handle_error("read()"); else if (ret == 0) break; else fputs(rcv_msg.buf, stdout); memset(&rcv_msg, 0, sizeof(rcv_msg)); memset(&send_msg, 0, sizeof(send_msg)); } close(sfd); return 0; } //回射服务器服务端 #include <string> #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <errno.h> #include "visiopacket.h" /* * TIME_WAIT触发:在与客户端建立TCP连接后,关闭服务侧,服务器会TIME_WAIT的状态,2个MSL时间后,才会释放掉连接。 * 解决服务重启后,不需要等待TIME_WAIT的问题: * int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); * */ /* * 僵尸进程:客户端关闭后,服务侧的子进程没有关闭 “63753 pts/19 00:00:00 refSrv <defunct>”; * 解决方法1:使用signal(SIGCHLD, SI_IGN)函数忽略僵尸进程; * 解决方法2: */ #define PORT 50001 #define ADDR ("127.0.0.1") int main() { signal(SIGCHLD, SIG_IGN); int sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd < 0) handle_error("socket()"); sockaddr_in s_sock, c_sock; socklen_t socklen = sizeof(sockaddr); s_sock.sin_family = AF_INET; s_sock.sin_port = htons(PORT); //inet_aton(ADDR, &s_sock.sin_addr); s_sock.sin_addr.s_addr = htonl(INADDR_ANY); int on = 1; if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) handle_error("setsockopt()"); if (bind(sfd, (sockaddr *)&s_sock, sizeof(sockaddr)) < 0) handle_error("bind()"); if (listen(sfd, 5) < 0) handle_error("listen()"); while (true) { int afd = accept(sfd, (sockaddr*)&c_sock, &socklen); printf("Client(%s:%d) connected!\n", inet_ntoa(c_sock.sin_addr), ntohs(c_sock.sin_port)); pid_t pid = fork(); if (pid < 0) handle_error("fork()"); else if (pid == 0) { close(sfd); msg rcvMsg; while (true) { memset(&rcvMsg, 0, sizeof(rcvMsg)); read(afd, &rcvMsg.length, sizeof(rcvMsg.length)); int ret = readn(afd, rcvMsg.buf, rcvMsg.length); if (ret < 0) handle_error("read()"); //else if (ret == 0) //break; else { printf("Receive length = %d\n", rcvMsg.length); fputs(rcvMsg.buf, stdout); writen(afd, &rcvMsg, rcvMsg.length + sizeof(rcvMsg.length)); } } handle_success("client close!\n"); } close(afd); } close(sfd); return 0; } /* * visiopacket.h * * Created on: 2019年5月8日 *



你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: