Linux网络编程——TCP和UDP通信 亦凉 2021-12-04 06:43 305阅读 0赞 * TCP协议流程图、TCP建立即时聊天 * TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天 * UDP协议流程图、UDP建立即时连接 * 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME\_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。 * SO\_RCVLOWAT设置接收缓冲区下限 1、TCP协议的流程图 服务端:socket---bind---listen---while(1)\{---accept---recv---send---close---\}---close 客户端:socket----------------------------------connect---send---recv-----------------close ![1556806258606][] TCP建立即时聊天 tcp\_client.c #include <func.h> int main(int argc,char* argv[]) { ARGS_CHECK(argc,3); int socketFd; socketFd=socket(AF_INET,SOCK_STREAM,0); ERROR_CHECK(socketFd,-1,"socket"); struct sockaddr_in ser; bzero(&ser,sizeof(ser)); ser.sin_family=AF_INET; ser.sin_port=htons(atoi(argv[2])); ser.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制转为32位的网络字节序 int ret; ret=connect(socketFd,(struct sockaddr*)&ser,sizeof(ser)); ERROR_CHECK(ret, -1, "connect"); printf("connect success\n"); char buf[128]={0}; fd_set rdset; while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(socketFd, &rdset); ret = select(socketFd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(socketFd, &rdset)){ bzero(buf, sizeof(buf)); ret = recv(socketFd, buf, sizeof(buf), 0); ERROR_CHECK(ret, -1, "recv"); if(ret == 0){ printf("byebye!\n"); break; } printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ memset(buf, 0, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(ret == 0){ printf("byebye!\n"); break; } ret = send(socketFd, buf ,strlen(buf) - 1, 0); ERROR_CHECK(ret, -1, "send"); } } close(socketFd); } tcp\_server.c #include <func.h> int main(int argc,char* argv[]) { ARGS_CHECK(argc,3); int socketFd; socketFd = socket(AF_INET,SOCK_STREAM,0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 int ret; ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息 int new_fd; struct sockaddr_in client; bzero(&client, sizeof(client)); int addrlen = sizeof(client); new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); ERROR_CHECK(new_fd, -1, "accept"); printf("client ip=%s, port=%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); fd_set rdset; char buf[128] = {0}; while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(new_fd, &rdset); ret = select(new_fd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(new_fd, &rdset)){ bzero(buf, sizeof(buf)); ret = recv(new_fd, buf, sizeof(buf), 0); ERROR_CHECK(ret, -1, "recv"); if(ret == 0){ printf("byebye!\n"); break; } printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ memset(buf, 0, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(ret == 0){ printf("byebye!\n"); break; } ret = send(new_fd, buf, strlen(buf) - 1, 0); ERROR_CHECK(ret, -1, "send"); } } close(new_fd); close(socketFd); return 0; } 2、TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天 tcp\_server.c #include <func.h> int main(int argc,char* argv[]) { ARGS_CHECK(argc,3); int socketFd; socketFd = socket(AF_INET,SOCK_STREAM,0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 int ret; ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息 int new_fd; struct sockaddr_in client; bzero(&client, sizeof(client)); int addrlen = sizeof(client); char buf[128] = {0}; fd_set rdset; fd_set needMonitorSet; //需要监听的描述符集合 FD_ZERO(&needMonitorSet); FD_SET(STDIN_FILENO, &needMonitorSet); FD_SET(socketFd, &needMonitorSet); while(1){ memcpy(&rdset, &needMonitorSet, sizeof(fd_set)); ret = select(11, &rdset, NULL, NULL, NULL); //设最大监控描述符为10 if(FD_ISSET(socketFd, &rdset)){ //如果监听到客户端则accept接受远程计算机的连接请求 new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); //accept函数接受一个连接时,会返回一个新的socket标识符,以后数据的数据传输和读取就要通过这个新的socket编号来处理,原来的socket继续监听其他客户机的连接请求。 ERROR_CHECK(new_fd, -1, "accept"); printf("client ip=%s, port=%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); FD_SET(new_fd, &needMonitorSet); } if(FD_ISSET(new_fd, &rdset)){ bzero(buf, sizeof(buf)); ret = recv(new_fd, buf, sizeof(buf), 0); ERROR_CHECK(ret, -1, "recv"); if(ret == 0){ printf("byebye!\n"); FD_CLR(new_fd, &needMonitorSet); //从needMonitorSet中删除new_fd close(new_fd); continue; } printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ memset(buf, 0, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(ret == 0){ printf("byebye!\n"); break; } ret = send(new_fd, buf, strlen(buf) - 1, 0); ERROR_CHECK(ret, -1, "send"); } } close(socketFd); return 0; } tcp\_client.c和1同 3、使用UDP协议的流程图 服务端:socket---bind---recvfrom---sendto---close 客户端:socket----------sendto---recvfrom---close ![1556806183309][] * sendto()函数原型: **int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr* to, int tolen);** 该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。 * recvfrom()函数原型: \*\*int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr* from,int \*fromlen);\*\* from是一个struct sockaddr类型的变量,该变量保存连接机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。 UDP传输一个数据报示例: udp\_server.c #include <func.h> int main(int argc, char **argv){ ARGS_CHECK(argc, 3); int socketFd; socketFd = socket(AF_INET, SOCK_DGRAM, 0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser,sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 int ret; ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); char buf[128] = {0}; struct sockaddr_in client; int addrlen = sizeof(client); ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen); ERROR_CHECK(ret, -1, "recvfrom"); printf("client ip = %s, port = %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); printf("udp server gets %s\n", buf); //bzero(buf, sizeof(buf)); //ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen); //ERROR_CHECK(ret, -1, "recvfrom"); //printf("udp server gets %s\n", buf); ret = sendto(socketFd, "world", 5, 0, (struct sockaddr*)&client, sizeof(client)); ERROR_CHECK(ret, -1, "sendto"); close(socketFd); return 0; } udp\_client.c #include <func.h> int main(int argc, char *argv[]){ ARGS_CHECK(argc, 3); int socketFd; socketFd = socket(AF_INET, SOCK_DGRAM, 0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]); int ret; ret = sendto(socketFd, "helloworld", 10, 0, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "sendto"); char buf[128] = {0}; ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL); ERROR_CHECK(ret, -1, "recvfrom"); printf("udp client gets %s\n", buf); close(socketFd); return 0; } 4、使用UDP协议建立即时连接 udp\_server.c #include <func.h> int main(int argc, char **argv){ ARGS_CHECK(argc, 3); int socketFd; socketFd = socket(AF_INET, SOCK_DGRAM, 0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser,sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 int ret; ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); char buf[128] = {0}; struct sockaddr_in client; int addrlen = sizeof(client); printf("client ip = %s, port = %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); fd_set rdset; while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(socketFd, &rdset); ret = select(socketFd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(socketFd, &rdset)){ bzero(buf, sizeof(buf)); ret = recvfrom(socketFd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &addrlen); ERROR_CHECK(ret, -1, "recvfrom"); printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ bzero(buf, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(0 == ret){ printf("byeybye\n"); break; } ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&client, sizeof(client)); ERROR_CHECK(ret, -1, "sendto"); } } close(socketFd); return 0; } udp\_client.c #include <func.h> int main(int argc, char *argv[]){ ARGS_CHECK(argc, 3); int socketFd; socketFd = socket(AF_INET, SOCK_DGRAM, 0); ERROR_CHECK(socketFd, -1, "socket"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]); int ret; char buf[128] = {0}; fd_set rdset; while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(socketFd, &rdset); ret = select(socketFd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(socketFd, &rdset)){ bzero(buf, sizeof(buf)); ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL); ERROR_CHECK(ret, -1, "recvfrom"); printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ bzero(buf, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(0 == ret){ printf("byeybye\n"); break; } ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "sendto"); } } close(socketFd); return 0; } 5、如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME\_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。 int reuse=1; setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&reuse,sizeof(int)); tcp\_server.c #include <func.h> int main(int argc,char* argv[]) { ARGS_CHECK(argc,3); int socketFd; socketFd = socket(AF_INET,SOCK_STREAM,0); ERROR_CHECK(socketFd, -1, "socket"); int ret; int reuse = 1; ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); ERROR_CHECK(ret, -1, "setsockopt"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息 int new_fd; struct sockaddr_in client; bzero(&client, sizeof(client)); int addrlen = sizeof(client); new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); ERROR_CHECK(new_fd, -1, "accept"); printf("client ip=%s, port=%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); fd_set rdset; char buf[128] = {0}; while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(new_fd, &rdset); ret = select(new_fd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(new_fd, &rdset)){ bzero(buf, sizeof(buf)); ret = recv(new_fd, buf, sizeof(buf), 0); ERROR_CHECK(ret, -1, "recv"); if(ret == 0){ printf("byebye!\n"); break; } printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ memset(buf, 0, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(ret == 0){ printf("byebye!\n"); break; } ret = send(new_fd, buf, strlen(buf) - 1, 0); ERROR_CHECK(ret, -1, "send"); } } close(new_fd); close(socketFd); return 0; } tcp\_client.c和1同 6、SO\_RCVLOWAT:接收缓冲区下限 tcp\_server\_rcvlowat.c #include <func.h> int main(int argc,char* argv[]) { ARGS_CHECK(argc,3); int socketFd; socketFd = socket(AF_INET,SOCK_STREAM,0); ERROR_CHECK(socketFd, -1, "socket"); int ret; int reuse = 1; ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); ERROR_CHECK(ret, -1, "setsockopt"); struct sockaddr_in ser; bzero(&ser, sizeof(ser)); ser.sin_family = AF_INET; ser.sin_port = htons(atoi(argv[2])); ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序 ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser)); ERROR_CHECK(ret, -1, "bind"); listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息 int new_fd; struct sockaddr_in client; bzero(&client, sizeof(client)); int addrlen = sizeof(client); new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); ERROR_CHECK(new_fd, -1, "accept"); printf("client ip=%s, port=%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); fd_set rdset; char buf[128] = {0}; int rcvLowAt = 10; ret=setsockopt(new_fd,SOL_SOCKET,SO_RCVLOWAT,&rcvLowAt,sizeof(int)); ERROR_CHECK(ret, -1, "setsockopt"); while(1){ FD_ZERO(&rdset); FD_SET(STDIN_FILENO, &rdset); FD_SET(new_fd, &rdset); ret = select(new_fd + 1, &rdset, NULL, NULL, NULL); if(FD_ISSET(new_fd, &rdset)){ bzero(buf, sizeof(buf)); ret = recv(new_fd, buf, sizeof(buf), 0); ERROR_CHECK(ret, -1, "recv"); if(ret == 0){ printf("byebye!\n"); break; } printf("%s\n", buf); } if(FD_ISSET(STDIN_FILENO, &rdset)){ memset(buf, 0, sizeof(buf)); ret = read(STDIN_FILENO, buf, sizeof(buf)); if(ret == 0){ printf("byebye!\n"); break; } ret = send(new_fd, buf, strlen(buf) - 1, 0); ERROR_CHECK(ret, -1, "send"); } } close(new_fd); close(socketFd); return 0; } tcp\_client.c和1同 转载于:https://www.cnblogs.com/Mered1th/p/10803634.html [1556806258606]: /images/20211204/2cf01fc71f794061a4cb2bd04d69a518.png [1556806183309]: /images/20211204/39ddb5de7f4f4bc39791a4d91dc8cee8.png
相关 网络编程:TCP/UDP通信问题解析 在计算机科学中,TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种主要的互联网通信协议。 1. 我不是女神ヾ/ 2024年09月19日 17:39/ 0 赞/ 1 阅读
相关 Java网络编程问题:TCP和UDP通信示例对比 在Java中,TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。而UDP(用户数据报协议)则是一种无连接的、不可靠的数据报传输层协议。 以下是两种协议的基本通信示例: 叁歲伎倆/ 2024年09月11日 12:27/ 0 赞/ 24 阅读
相关 Java网络编程:TCP和UDP通信的差异 在Java网络编程中,TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种主要的通信协议。 1. 电玩女神/ 2024年09月10日 21:51/ 0 赞/ 22 阅读
相关 【网络编程】——UDP和TCP通信方式 公司有计划打算做消息推送,自己也是闲着没事稍微了解一下,从基础开始学习以便真正使用的时候不用太过紧张吧。消息推送在各种应用非常的常见,这是网络编程的一种,还是从根本上开 缺乏、安全感/ 2022年06月16日 11:26/ 0 赞/ 221 阅读
相关 Java网络编程(基于TCP和UDP的通信实现) 简单说一些常见的协议所属的OIS层: 应用层:远程登录协议Telnet、文件传输协议FTP,超文本传输协议HTTP,域名服务DNS,简单邮件传输协议SMTP,邮局协议POP 墨蓝/ 2022年06月01日 06:15/ 0 赞/ 159 阅读
相关 网络通信协议、UDP与TCP协议、UDP通信、TCP通信 计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传 妖狐艹你老母/ 2021年12月21日 00:47/ 0 赞/ 383 阅读
相关 网络通信协议、UDP与TCP协议、UDP通信、TCP通信 计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传 约定不等于承诺〃/ 2021年12月20日 15:29/ 0 赞/ 435 阅读
相关 Linux网络编程——TCP和UDP通信 TCP协议流程图、TCP建立即时聊天 TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天 UDP协议流程图、UDP建立即时连 亦凉/ 2021年12月04日 06:43/ 0 赞/ 306 阅读
还没有评论,来说两句吧...