linux——UDP编程
UDP 是发短信TCP 是打电话。TCP 打电话必须先拨号connect、接通accept才能说话全程不断开。UDP 发短信不需要接通直接填对方号码 → 发送对方直接收。没有连接、没有握手、没有断开。1、UDP 最核心特点无连接不用 listen、不用 accept、不用 connect服务端不用。数据报模式一条一条发一条一条收。不可靠发出去不管对方收没收到。速度快支持一对多、多对多广播、组播2、UDP 服务端 固定 4 步1. socket()创建 UDP 套接字socket(AF_INET, SOCK_DGRAM, 0);SOCK_DGRAM→ 代表 UDP2. bind()绑定 IP 和端口和 TCP 一模一样告诉系统我要在这个端口收数据。3. recvfrom()阻塞接收数据谁发来都行来了就收。同时获取客户端 IP 端口。4. close()关闭套接字。3、UDP 客户端 固定 3 步socket()sendto () → 直接发给服务器close()客户端甚至可以不用 bind系统自动分配端口。4、UDP 最重要的两个函数1. recvfrom服务端收数据recvfrom( sockfd, buf, len, 0, (struct sockaddr *)client_addr, // 客户端地址 addrlen );作用阻塞等数据收到数据同时知道谁发的。2. sendto客户端发数据sendto( sockfd, buf, len, 0, (struct sockaddr *)server_addr, // 目的地址 addrlen );作用直接把数据发给目标 IP 端口5、UDP 服务端流程plaintextsocket → bind → 循环 recvfrom 收数据 → 处理没有 listen没有 accept没有连接谁发数据给这个端口服务器就收谁的。6、TCP vs UDP 函数对比步骤TCPUDP创建socket(…SOCK_STREAM…)socket(…SOCK_DGRAM…)绑定bindbind监听listen无连接accept / connect无收数据readrecvfrom发数据writesendto服务器端#includestdio.h #includestdlib.h #includeunistd.h #includestring.h #includestrings.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include arpa/inet.h #define SERV_PORT 5001 #define BUFSIZE 1024 #define QUIT_STR QUIT int main() { int fd -1; struct sockaddr_in sin; //1.socket fd socket(AF_INET,SOCK_DGRAM,0); if(fd 0) { perror(socket); exit(1); } int b_reuser 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,b_reuser,sizeof(int)); //2.bind bzero(sin,sizeof(sin)); sin.sin_family AF_INET; sin.sin_port htons(SERV_PORT); sin.sin_addr.s_addr INADDR_ANY; if(bind(fd,(struct sockaddr*)sin,sizeof(sin)) 0) { perror(bind); exit(1); } //3.recvfrom char buf[BUFSIZE]; struct sockaddr_in cin; socklen_t addrlen sizeof(cin); while(1) { bzero(buf,BUFSIZE); if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr*)cin,addrlen) 0) { perror(recvfrom); continue; } char ipv4_addr[16]; if(!inet_ntop(AF_INET,(void *)cin.sin_addr,ipv4_addr,sizeof(cin))) { perror(inet_ntop); exit(1); } printf(receive from(%s,%d),data:%s\n,ipv4_addr,ntohs(cin.sin_port),buf); if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) { printf(client(%s:%d)is exiting!\n,ipv4_addr,ntohs(cin.sin_port)); } } close(fd); return 0; }①socket () 创建 UDP 套接字fd socket(AF_INET, SOCK_DGRAM, 0);重点AF_INETIPv4SOCK_DGRAM数据报 →UDP之前 TCP 是SOCK_STREAM这一句决定了是 UDP 还是 TCP②setsockopt()端口复用int b_reuser 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, b_reuser, sizeof(int));作用允许程序重启后立刻绑定同一个端口不加这句程序退出后端口会被占用几十秒。③bind()绑定IP端口sin.sin_family AF_INET; sin.sin_port htons(SERV_PORT); sin.sin_addr.s_addr htonl(INADDR_ANY); bind(fd, (struct sockaddr*)sin, sizeof(sin));和 TCP 完全一样绑定端口 5001绑定所有网卡 INADDR_ANY④最核心UDP 怎么收数据UDP 没有 accept直接收while(1) { bzero(buf, BUFSIZE); // 阻塞等待接收 UDP 数据报 recvfrom( fd, buf, BUFSIZE-1, 0, (struct sockaddr*)cin, addrlen ); }recvfrom () 函数函数作用阻塞等待客户端发来 UDP 数据报谁发来的发来什么内容全部拿到参数fdUDP 套接字buf接收数据的缓冲区BUFSIZE-1最大接收长度0默认为 0cin 用来保存客户端的 IP 和端口addrlen地址长度⑤接收后打印客户端信息inet_ntop(AF_INET, cin.sin_addr, ipv4_addr, sizeof(ipv4_addr)); printf(receive from(%s,%d), data:%s\n, ipv4_addr, ntohs(cin.sin_port), buf);⑥判断退出if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))) { printf(client exiting\n); }收到QUIT就提示客户端退出。客户端#includestdio.h #includestdlib.h #includeunistd.h #includestring.h #includestrings.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include arpa/inet.h #define SREV_PORT 5001 #define SERV_IP_ADDR 192.168.88.129 #define QUIT_STR QUIT #define BUFSIZE 1024 int main(int argc,char **argv) { if(argc ! 3) { exit(1); } int fd socket(AF_INET,SOCK_DGRAM,0); if(fd 0) { perror(socket); exit(1); } int port -1; port atoi(argv[2]); struct sockaddr_in sin; bzero(sin,sizeof(sin)); sin.sin_family AF_INET; sin.sin_port htons(port); sin.sin_addr.s_addr inet_addr(argv[1]); char buf[BUFSIZE]; while(1) { if(fgets(buf,BUFSIZE-1,stdin) NULL) { perror(fgets); continue; } sendto(fd,buf,strlen(buf),0,(struct sockaddr*)sin,sizeof(sin)); if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) { printf(client is exited!\n); break; } } close(fd); return 0; }①主函数命令行参数int main(int argc,char **argv) { if(argc ! 3) { exit(1); }作用必须传入 2 个参数服务器 IP 端口②创建UDP套接字int fd socket(AF_INET,SOCK_DGRAM,0); if(fd 0) { perror(socket); exit(1); }重点AF_INETIPv4SOCK_DGRAMUDP 数据报TCP 是SOCK_STREAM③读取命令行端口int port atoi(argv[2]);把命令行的字符串端口→转成数字④配置服务器地址填短信收件人struct sockaddr_in sin; bzero(sin,sizeof(sin)); sin.sin_family AF_INET; sin.sin_port htons(port); sin.sin_addr.s_addr inet_addr(argv[1]);作用填写目标服务器的 IP 和端口。sin服务器地址结构体argv[1]服务器 IPport服务器端口⑤循环发送数据char buf[BUFSIZE]; while(1) { // 从键盘读入数据 if(fgets(buf,BUFSIZE-1,stdin) NULL) { perror(fgets); continue; } // 发送数据UDP 核心函数 sendto(fd,buf,strlen(buf),0,(struct sockaddr*)sin,sizeof(sin)); // 如果输入 QUIT退出 if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) { printf(client is exited!\n); break; } }sendto()函数sendto( fd, // UDP 套接字 buf, // 要发送的数据 strlen(buf),// 数据长度 0, // 默认为 0 (struct sockaddr*)sin, // 服务器地址 sizeof(sin) // 地址长度 );作用直接把数据发送给指定的服务器不需要连接不需要 connect参数fdUDP 套接字buf接收数据的缓冲区BUFSIZE-1最大接收长度0默认为 0cin 用来保存客户端的 IP 和端口addrlen地址长度⑥关闭套接字退出close(fd); return 0;执行结果