Linux应用层开发 --- UDPTCP
发布时间:2026/9/3 5:37:55来源:尧图网络
一、UDP协议UDP协议User Datagrams Protocal即用户数据报协议。位于传输层一UDP特点1. 无连接2. 面向数据报3. 尽最大努力交付不安全不可靠丢包、乱序4. 机制简单实时性好效率高直播、游戏画面二网络通信模型B/SBrowser/Server浏览器/服务器- 通用客户端- 数据资源基本都保存在服务端- 只需要开发服务端C/SClient/Server客户端/服务端- 专用客户端- 客户端和服务端都可以保存资源- 客户端和服务端都需要开发三UDP通信1. 客户端操作流程1socket()创建socket套接字套接字应用层网络通信的端口本质是文件描述符#include sys/types.h /* See NOTES */#include sys/socket.hint socket(int domain, int type, int protocol);功能创建通信的套接字参数domain使用的协议簇AF_INETIPv4网络层AF_INET6IPv6type传输层的协议类型SOCK_STREAMTCPSOCK_DGRAMUDPprotocol0 默认方式返回值成功文件描述符套接字失败-12sendto()发送UDP报文ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);功能发送udp数据报文参数sockfd套接字buf要发送的数据首地址len发送的数据字节数flags0 按照默认方式发送dest_addr服务器的地址信息IPPORTaddrlen地址信息的长度返回值成功发送的字节数失败-1struct sockaddr_in {sa_family_t sin_family; /* address family: AF_INET */in_port_t sin_port; /* port in network byte order */struct in_addr sin_addr; /* internet address */};//新定义的与struct sockaddr兼容网络字节序大端内存高地址存低字节位低地址存高字节位主机字节序小端内存高地址存高字节位低地址存低字节位这个是小端存储网络字节序和主机字节序的转换host---net sshort 2字节 l:long 4字节htons2字节数据主机字节序转网络字节序htonl4字节数据主机字节序转网络字节序ntohs2字节数据网络字节序转主机字节序ntohl4字节数据网络字节序转主机字节序相关函数in_addr_t inet_addr(const char *cp);功能将点分十进制的字符串IP地址转换成32位的二进制IP地址形式参数cp字符串IP地址返回值整型IP地址3recvfrom()接收UDP报文ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);功能接收UDP数据报文参数sockfd套接字buf存储网络接收到的数据lrn期望接收到的字节数flags0 默认方式接收src_addr保存发送方地址信息的指针addrlen地址长度的指针返回值成功实际接收到的字节数失败-10当对方发送0字节数据包4close()关闭套接字2. 服务端操作流程1socket()创建socket套接字2bind()绑定服务端的IP和端口号#include sys/types.h /* See NOTES */#include sys/socket.hint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能绑定当前套接字和IP地址及端口号参数sockfd套接字addr要绑定的地址信息指针addrlen地址信息的长度返回值成功0失败-13recvfrom()接收UDP报文4sendto()发送UDP报文5close()关闭套接字UDP报文头部源端口号4字节目标端口号8字节数据包长度2字节检验和Checksum2字节示例client.c#include stdio.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include arpa/inet.h #include netinet/in.h #include netinet/ip.h /* superset of previous */ #include unistd.h #include string.h int main(void) { //socket() //sendto(); //recvfrom(); //close(); int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (-1 sockfd) { perror(socket error); return -1; } struct sockaddr_in seraddr; seraddr.sin_family AF_INET; seraddr.sin_port htons(50000); //接收方的端口号 seraddr.sin_addr.s_addr inet_addr(192.168.0.XXX); //接收方的IP地址 char buff[1024] {0}; while (1) { fgets(buff, sizeof(buff), stdin); ssize_t size sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)seraddr, sizeof(seraddr)); if (-1 size) { perror(sendto error); return -1; } printf(size %ld\n, size); memset(buff, 0, sizeof(buff)); size recvfrom(sockfd, buff, sizeof(buff), 0, NULL ,NULL); if (size 0) { printf(recvfrom error); return -1; } printf(ser--cli : %s\n, buff); } close(sockfd); return 0; }server.c#include head.h int main(void) { //socket() //bind() //recvfrom() //sendto() //close() int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket error); return -1; } struct sockaddr_in cliaddr; socklen_t clilen sizeof(cliaddr); struct sockaddr_in seraddr; seraddr.sin_family AF_INET; seraddr.sin_port htons(50000); seraddr.sin_addr.s_addr inet_addr(192.168.0.XXX);//接收方IP地址 int ret bind(sockfd, (struct sockaddr *)seraddr, sizeof(seraddr)); if (ret 0) { perror(bind error); return -1; } char buff[1024] {0}; while (1) { memset(buff, 0, sizeof(buff)); ssize_t size recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)cliaddr, clilen); if (size 0) { perror(recvfrom error); return -1; } printf([%s : %d][%ld] %s\n, inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port),size, buff); strcat(buff, ----ok!); sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)cliaddr, clilen); } close(sockfd); return 0; }二、TCP协议TCP即传输控制协议流式套接字位于传输层一TCP特点1. 有连接通信2. 面向字节流3. 安全可靠的传输协议三次握手、四次挥手、应答机制、超时重传机制等4. 机制复杂实时性和效率没有UDP高HTTPS、MQTT、FTP二TCP的三次握手和四次挥手机制1. 三次握手TCP建立连接时通过三次握手来确保双方都已经准备就绪。三次握手由客户端发起数据收发2. 四次挥手TCP都拿开连接时通过四次挥手确保通信双方数据都已经收发结束。四次挥手可以由客户端发起也可以由服务端发起四次挥手机制1应答机制TCP为发送的数据进行编号发送数据时报文头部的序列号是这包数据的第一个数据的编号将来接收方需要给这包数据发送ACKACK报文中确认号是收到的最后一个字节编号12超时重传机制TCP每发送一包数据后都要等待应答如果超时时间之内没有收到应答则重新发送这包数据3滑动窗口机制缓冲区保存已发送并收到应答的数据、已发送未收到应答的数据、未发送但在对方处理范围内的数据4延迟应答机制TCP可以发送多组数据发送的同时等待应答5流量控制机制TCP会根据发送端的数据处理和接收能力调整自己的发送速率根据ACK中窗口值的大小进行动态调整流量6捎带应答机制ACK可以和应用层发送的数据一起发出表示对上包数据的响应标志位TCP报文头部数据位1SYN请求建立连接标志位2ACK响应报文标志位3PSH携带数据的报文标志位4FIN请求断开连接标志位三TCP通信1. 客户端socket()---connect()---send()---recv()---close()1connect();int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);功能请求建立连接参数sockfd套接字addr 服务端的地址addrlen地址长度返回值成功0失败-12recv();ssize_t recv(int sockfd, void *buf, size_t len, int flags);功能接收网络数据参数sockfd通讯套接字buf存放接收到的数据的空间首地址len期待接收到的字节数flags 0 默认方式返回值成功返回实际收到的字节数失败-10发送断开连接2. 服务端socket()---bind()---listen()---accept()---send()---close()1listen();int listen(int sockfd, int backlog);功能监听客户端的三次握手参数sockfd监听套接字backlog最多允许监听的客户端的个数返回值成功0失败-12accept();int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能接收完成三次握手的客户端并返回一个通讯套接字参数sockfd监听套接字addr保存接入的客户端的地址信息的指针addrlen地址信息长度的指针返回值成功通讯套接字失败-1示例client.c#include head.h int main(void) { int sockfd socket(AF_INET, SOCK_STREAM, 0); if (-1 sockfd) { perror(socket error); return -1; } struct sockaddr_in seraddr; seraddr.sin_family AF_INET; seraddr.sin_port htons(50000); seraddr.sin_addr.s_addr inet_addr(192.168.0.XXX); //接收方IP地址 int ret connect(sockfd, (struct sockaddr *)seraddr, sizeof(seraddr)); if (-1 ret) { perror(connect error); return -1; } send(sockfd, hello world, 11, 0); close(sockfd); return 0; }server.c#include head.h //socket() //bind() //listen() //accept(); //recv(); //send(); //close() int main(void) { int sockfd socket(AF_INET, SOCK_STREAM, 0); if (-1 sockfd) { perror(socket error); return -1; } struct sockaddr_in cliaddr; socklen_t clilen sizeof(cliaddr); struct sockaddr_in seraddr; seraddr.sin_family AF_INET; seraddr.sin_port htons(50000); seraddr.sin_addr.s_addr inet_addr(192.168.0.XXX); //接收方IP地址 int ret bind(sockfd, (struct sockaddr *)seraddr, sizeof(seraddr)); if (-1 ret) { perror(bind error); return -1; } ret listen(sockfd, 10); if (-1 ret) { perror(listen error); return -1; } int connfd accept(sockfd, (struct sockaddr *)cliaddr, clilen); if (-1 connfd) { perror(accept error); return -1; } printf([%s : %d] get online!\n, inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); char buff[1024] {0}; ssize_t size recv(connfd, buff, sizeof(buff), 0); if (-1 size) { perror(recv error); return -1; } printf(buff: %s\n, buff); close(connfd); close(sockfd); return 0; }四TCP报文头部标志位TCP报文头部数据位1SYN请求建立连接标志位2ACK 效应报文标志位3PSH携带数据的报文标志位4FIN请求断开连接标志位5URG紧急数据标志位6RST重置标志位三、网络抓包工具wireshark网络抓包工具可以抓取经过网卡的网络数据从而分析网络问题Linux下安装wiresharksudo apt-get install wireshark抓包流程1启动wiresharksudo wireshark2选择抓取的网卡any3选择抓取数据包的过滤条件并触发udp.port 500004进行网络通信粘包问题粘包发送端发送速度太快接收端处理速度比较慢导致数据在缓冲区缓存应用层读出数据时多包数据发送生了粘连解决粘包的方法1收发指定大小数据收发结构体注意跨平台发送时平台的位数struct data{xxx;long num;};send(sockfd, data, sizeof(struct data), 0);recv(sockfd, data, sizeof(struct data), 0);2给发送的数据明显的分隔符hello\nworld\nhello\nworld\n3以自定义方式定义发送的数据帧格式帧头 数据长度 消息类型 校验 帧尾常用帧头5A、A5、7E、E7、EB90等消息类型 区分数据包的数据类型校验8位和校验checksum16位和校验CRC校验也会用明显的开始AA或结束BB标志位另外可添加消息长度标识AA 0C 01 00 00 00 10 00 00 01 00 00 00 11 00 sum BBAA 0F 02 00 00 00 01 00 00 00 10 00 00 01 00 00 00 11 00 sum BBAA 0C 01 00 00 00 10 00 00 01 00 00 BB 11 00 sum BB AA 0F 02 00 00 00 01 0000 00 10 00 00 01 00 00 00 11 00 BB
网站建设高端定制企业官网