网络编程中的UDP通信
来源:互联网 发布:淘宝买东西物流不更新 编辑:程序博客网 时间:2024/05/17 02:47
之前的网络编程主要针对的是TCP协议,现在总结一下学习UDP协议的知识点!
基本概念
传输层主要应用的协议模型有两种,一种是TCP协议,另外一种则是UDP协议。TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。
相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。
那么与我们熟知的TCP相比,UDP有哪些优点和不足呢?由于无需创建连接,所以UDP**开销较小,数据传输速度快,实时性较强。多用于对实时性要求较高的通信场合,如视频会议、电话会议等。但随之也伴随着数据传输不可靠,传输数据的正确率、传输顺序和流量都得不到控制和保证。所以,通常情况下,使用UDP协议进行数据传输,为保证数据的正确性,我们需要在应用层添加辅助校验协议来弥补UDP的不足**,以达到数据可靠传输的目的。
与TCP类似的,UDP也有可能出现缓冲区被填满后,再接收数据时丢包的现象。由于它没有TCP滑动窗口的机制,通常采用如下两种方法解决:
1) 服务器应用层设计流量控制,控制发送数据速度。
2) 借助setsockopt函数改变接收缓冲区大小。如:
#include <sys/socket.h>int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int n = 220x1024 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
特点
UDP的特点
- 无连接
- 基于消息的数据传输服务,不是字节流
- 不可靠
- 一般情况下UDP更加高效
常用API
recvfrom函数
相关函数:recv, recvmsg, send, sendto, socket
头文件:
#include <sys/types.h> #include <sys/socket.h>
定义函数:int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from,int *fromlen);
函数说明:recv()用来接收远程主机经指定的socket 传来的数据, 并把数据存到由参数buf 指向的内存空间, 参数len 为可接收数据的最大长度. 参数flags 一般设0, 其他数值定义请参考recv(). 参数from 用来指定欲传送的网络地址, 结构sockaddr 请参考bind(). 参数fromlen 为sockaddr 的结构长度.
返回值:成功则返回接收到的字符数, 失败则返回-1, 错误原因存于errno 中.
错误代码:
EBADF 参数s 非合法的socket 处理代码
EFAULT 参数中有一指针指向无法存取的内存空间.
ENOTSOCK 参数s 为一文件描述词, 非socket.
EINTR 被信号所中断.
EAGAIN 此动作会令进程阻断, 但参数s 的socket 为不可阻断.
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确.
sendto函数
相关函数:send, sendmsg, recv, recvfrom, socket
头文件:
#include <sys/types.h> #include <sys/socket.h>
定义函数:int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);
函数说明:sendto() 用来将数据由指定的socket 传给对方主机. 参数s 为已建好连线的socket, 如果利用UDP协议则不需经过连线操作. 参数msg 指向欲连线的数据内容, 参数flags 一般设0, 详细描述请参考send(). 参数to 用来指定欲传送的网络地址, 结构sockaddr 请参考bind(). 参数tolen 为sockaddr 的结果长度.
返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 中.
错误代码:
1、EBADF 参数s 非法的socket 处理代码.
2、EFAULT 参数中有一指针指向无法存取的内存空间.
3、WNOTSOCK canshu s 为一文件描述词, 非socket.
4、EINTR 被信号所中断.
5、EAGAIN 此动作会令进程阻断, 但参数s 的soket 为补课阻断的.
6、ENOBUFS 系统的缓冲内存不足.
7、EINVAL 传给系统调用的参数不正确.
setsockopt作用:
端口复用。
setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
设置缓冲区大小
int n = 220x1024
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));开放广播权限
setsockopt(sock_fd,SOL_SOCKET,SO_BROADCAST,&flag,sizeof(flag));
开放组播权限
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));
加入组播组。
setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
关于设置组播地址的API和结构体
inet_pton
–man命令查看帮助信息struct ip_mreqn group;
–grep命令查看定义
UDP会用到的其他函数
- bind
- connect
- send
可以参考前面TCP相关的介绍和man文档
UDP经典的C/S模型
由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在应用层实现。
编译运行server,在两个终端里各开一个client与server交互,看看server是否具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,看此时client还能否和server联系上。和前面TCP程序的运行结果相比较,体会无连接的含义。
- 客户端程序
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define MAX_BUF 80#define SRV_PORT 8001 int main(){ int ret = 0; struct sockaddr_in srv_addr; int sock_fd; char buf[MAX_BUF]={0}; socklen_t addr_len; //创建UDP套接字 sock_fd = socket(AF_INET,SOCK_DGRAM,0); //填充服务器地址 bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //获取地址长度 addr_len = sizeof(srv_addr); //从标准输入获取数据发送给服务器并等待服务器返回数据 while(fgets(buf,sizeof(buf),stdin) != NULL) { addr_len = sizeof(srv_addr); //sendto第一次发送的时候,会绑定地址,需要指定对方的地址 ret = sendto(sock_fd,buf,strlen(buf),0,(struct sockaddr*)&srv_addr,sizeof(srv_addr));//开始发送 if(ret == -1) { perror("sendto"); exit(-1); } ret = recvfrom(sock_fd,buf,sizeof(buf),0,(struct sockaddr*)&srv_addr,&addr_len);//等待服务器反馈数据 if(ret == -1) { if (errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("recv from IP:%s,IP:%d\n",inet_ntoa(srv_addr.sin_addr),ntohs(srv_addr.sin_port)); fputs(buf,stdout);//打印接收到的数据 memset(buf,0,sizeof(buf));//清空缓冲区 } return 0;}
- 服务器端程序
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define MAX_BUF 80#define SRV_PORT 8001 int main(){ struct sockaddr_in srv_addr,clt_addr; socklen_t addr_len; int sock_fd; int ret = 0; int i= 0; char buf[MAX_BUF]={0}; sock_fd = socket(AF_INET,SOCK_DGRAM,0); if(-1 == sock_fd) { perror("socket"); exit(-1); } bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(sock_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr)); if(ret == -1) { perror("bind"); exit(-1); } addr_len = sizeof(clt_addr); printf("Accepting connections ...\n"); while(1) { addr_len = sizeof(clt_addr); memset(buf,0,sizeof(buf)); ret = recvfrom(sock_fd,buf,sizeof(buf),0,(struct sockaddr*)&clt_addr,&addr_len); if(ret == -1) { if (errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("recv from IP:%s,IP:%d\n",inet_ntoa(clt_addr.sin_addr),ntohs(clt_addr.sin_port));//打印对方地址 fputs(buf,stdout); for(i = 0;i < ret ;i++) buf[i] = toupper(buf[i]); ret = sendto(sock_fd,buf,ret,0,(struct sockaddr*)&clt_addr,sizeof(clt_addr)); if(-1 == ret) { perror("sendto"); exit(-1); } } close(sock_fd); return 0;}
UDP广播通信
在一个局域网内,如果一台主机想给所有本地网络中的所有其他主机发送数据,可以使用广播,比如多媒体教学,视频会议这类!
广播有特殊的IP地址,如果网关是192.168.1.1的话,广播地址就是192.168.1.255,套接字默认是没有开启广播权限的,需要使用setsockopt函数获取该权限。
- 服务器端代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define MAX_BUF 80#define SRV_PORT 8001 #define CLT_PORT 9000#define BRD_ADDR "192.168.1.255"int main(){ struct sockaddr_in srv_addr,clt_addr; int sock_fd; int ret = 0; int i= 0; int flag = 1; char buf[MAX_BUF]={0}; sock_fd = socket(AF_INET,SOCK_DGRAM,0); if(-1 == sock_fd) { perror("socket"); exit(-1); } bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT);//服务器端可以是一个随机的端口--在不接收客户端反馈数据的情况下 srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //获取广播权限--默认没有开启 setsockopt(sock_fd,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&flag,sizeof(flag)); ret = bind(sock_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr)); if(ret == -1) { perror("bind"); exit(-1); } //接收端的地址变得很重要--要指定接收端的端口号--双方端口要一致 bzero(&clt_addr,sizeof(clt_addr)); clt_addr.sin_family = AF_INET; clt_addr.sin_port = htons(CLT_PORT); clt_addr.sin_addr.s_addr = inet_addr(BRD_ADDR); while(1) { sprintf(buf,"Fuck it %d times\n",i++); ret = sendto(sock_fd,buf,strlen(buf),0,(struct sockaddr*)&clt_addr,sizeof(clt_addr)); if(-1 == ret) { perror("sendto"); exit(-1); } sleep(1); } close(sock_fd); return 0;}
- 客户端代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define MAX_BUF 80#define SRV_PORT 8001 #define CLT_PORT 9000#define SRV_ADDR "127.0.0.1"int main(){ int ret = 0; struct sockaddr_in local_addr,srv_addr; int sock_fd; char buf[MAX_BUF]={0}; socklen_t addr_len; //创建UDP套接字 sock_fd = socket(AF_INET,SOCK_DGRAM,0); //填充服务器地址 bzero(&local_addr,sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(CLT_PORT); local_addr.sin_addr.s_addr = htonl(INADDR_ANY); bzero(&srv_addr,sizeof(srv_addr)); ret = bind(sock_fd,(struct sockaddr *)&local_addr,sizeof(local_addr)); if(ret == -1) { perror("bind"); exit(-1); } printf("Waitting broadcast ...\n"); while(1) { ret = recvfrom(sock_fd,buf,sizeof(buf),0,(struct sockaddr*)&srv_addr,&addr_len);//等待服务器反馈数据 if(ret == -1) { if (errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("recv from IP:%s,IP:%d\n",inet_ntoa(srv_addr.sin_addr),ntohs(srv_addr.sin_port)); fputs(buf,stdout);//打印接收到的数据 memset(buf,0,sizeof(buf));//清空缓冲区 } close(sock_fd); return 0;}
UDP组播/多播通信
组播出现的原因是广播有可能造成网络污染–局域网内有的主机并不想收到来自服务器的广播,只是某些个主机需要,所以需要对网络进行细分,进行分组管理分组发送数据,这样既高效又避免多余的数据包在网络里乱串,另一方面提高了服务器端组播数据的安全性。
组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
224.0.0.0~224.0.0.255 为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255 是公用组播地址,可以用于Internet;欲使用需申请。
224.0.2.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255 为本地管理组播地址,仅在特定的本地范围内有效。
可使用ip ad命令查看网卡编号,如:
$ ip ad1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 link/ether 00:0c:29:0a:c4:f4 brd ff:ff:ff:ff:ff:ff inet6 fe80::20c:29ff:fe0a:c4f4/64 scope link valid_lft forever preferred_lft forever
if_nametoindex 函数可以根据网卡名,获取网卡序号。
如果出现setsockopt“找不到设备“的情况,请参考:
http://blog.csdn.net/dengjin20104042056/article/details/52357157
以及http://blog.csdn.net/air_penguin/article/details/51350910
如果是出现段错误(Core dump),一般用GDB调试可以发现问题所在,一般是在inet_pton(AF_INET,"0.0.0.0",&group.imr_address);//本地任意IP进行组播--向哪一个组播组发送数据
出错,要注意要显示使用“0.0.0.0”,不能使用INADDR_ANY宏定义。
* 服务器端代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <net/if.h>#define MAX_BUF 80#define SRV_PORT 8001 #define CLT_PORT 9000#define GROUP "239.0.0.2"int main(){ struct sockaddr_in srv_addr,clt_addr; int sock_fd; int ret = 0; int i= 0; struct ip_mreqn group; char buf[MAX_BUF]={0}; sock_fd = socket(AF_INET,SOCK_DGRAM,0); if(-1 == sock_fd) { perror("socket"); exit(-1); } bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT);//服务器端可以是一个随机的端口--在不接收客户端反馈数据的情况下 srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(sock_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr));//绑定服务器地址 if(ret == -1) { perror("bind"); exit(-1); } inet_pton(AF_INET,GROUP,&group.imr_multiaddr);//设置组播组IP地址 inet_pton(AF_INET,"0.0.0.0",&group.imr_address);//本地任意IP进行组播--向哪一个组播组发送数据 group.imr_ifindex = if_nametoindex("eth0");//通过网卡名字获取网卡编号 //设置组播权限--后面两个参数变复杂了--要注意不是简单0或者1 setsockopt(sock_fd,IPPROTO_IP,IP_MULTICAST_IF,&group,sizeof(group)); //接收端的地址变得很重要--要指定接收端的端口号--双方端口要一致 bzero(&clt_addr,sizeof(clt_addr)); clt_addr.sin_family = AF_INET; clt_addr.sin_port = htons(CLT_PORT); clt_addr.sin_addr.s_addr = inet_addr(GROUP); while(1) { sprintf(buf,"Fuck it %d times\n",i++); ret = sendto(sock_fd,buf,strlen(buf),0,(struct sockaddr*)&clt_addr,sizeof(clt_addr)); if(-1 == ret) { perror("sendto"); exit(-1); } sleep(1); } close(sock_fd); return 0;}
- 客户端代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <net/if.h>#define MAX_BUF 80#define SRV_PORT 8001 #define CLT_PORT 9000#define GROUP "239.0.0.2"int main(){ int ret = 0; struct sockaddr_in local_addr,srv_addr; int sock_fd; char buf[MAX_BUF]={0}; socklen_t addr_len; struct ip_mreqn group;//组播IP+自己IP+网卡编号 //创建UDP套接字 sock_fd = socket(AF_INET,SOCK_DGRAM,0); //填充服务器地址 bzero(&local_addr,sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(CLT_PORT);//强行指定端口号--否则服务器广播/组播以后不知道如何和对等方的进程通信--必须要端口相同 local_addr.sin_addr.s_addr = htonl(INADDR_ANY); bzero(&srv_addr,sizeof(srv_addr)); //强行绑定本机地址--要明确自己的IP地址和端口才能进行广播和组播 //不同于TCP/IP或者简单的C/S模型,一旦涉及到广播和组播--必须要绑定自己的地址 ret = bind(sock_fd,(struct sockaddr *)&local_addr,sizeof(local_addr)); if(ret == -1) { perror("bind"); exit(-1); } inet_pton(AF_INET,GROUP,&group.imr_multiaddr);//设置组播组IP地址 inet_pton(AF_INET,"0.0.0.0",&group.imr_address);//使用本地/客户端IP添加到组播组--本地IP成为组播里面的一员--和服务器端不太一样 group.imr_ifindex = if_nametoindex("eth0");//通过网卡名字获取网卡编号 //设置client加入组播组--后面两个参数变复杂了--要注意不是简单0或者1 setsockopt(sock_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group)); printf("Waitting multicast ...\n"); while(1) { ret = recvfrom(sock_fd,buf,sizeof(buf),0,(struct sockaddr*)&srv_addr,&addr_len);//等待服务器反馈数据 if(ret == -1) { if (errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("recv from IP:%s,IP:%d\n",inet_ntoa(srv_addr.sin_addr),ntohs(srv_addr.sin_port)); fputs(buf,stdout);//打印接收到的数据 memset(buf,0,sizeof(buf));//清空缓冲区 } close(sock_fd); return 0;}
UDP注意点
- UDP报文可能会丢失、重复 、乱序
- UDP缺乏流量控制
- UDP协议数据报文截断
如果接收到的数据报,大于缓冲区;报文可以被截断;后面的部分会丢失。 - recvfrom返回0,不代表连接关闭,因为udp是无连接的。
- sendto可以发送数据0包。。。只含有udp头部。
- ICMP异步错误
观察现象: 关闭udp服务端,若启动udp客户端,从键盘接受数据后,再发送数据。udp客户端阻塞在sendto位置;- 说明1:udp发送报文的时,只把数据copy到发送缓冲区。在服务器没有起来的情况下,可以发送成功。
- 说明2:所谓ICMP异步错误是指:发送的报文的时候,没有错误,接受报文recvfrom的时候,会收到ICMP应答
- 说明3:异步的错误,是无法返回未连接的套接字。
- udp也可以调用connect
- 说明1:udp调用connet,并没有三次握手,只是维护了一个状态信息(和对等方的)。。。
- 说明2)一但调用connect,就可以使用send函数
关于UDP使用connect调用send函数的示例代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define MAX_BUF 80#define SRV_PORT 8001 int main(){ int ret = 0; struct sockaddr_in srv_addr; int sock_fd; char buf[MAX_BUF]={0}; socklen_t addr_len; //创建UDP套接字 sock_fd = socket(AF_INET,SOCK_DGRAM,0); //填充服务器地址 bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //获取地址长度 addr_len = sizeof(srv_addr); //udp 也可以 调用connet //udp调用connet,并没有三次握手,只是维护了一个状态信息(和对等方的) connect(sock_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)); //从标准输入获取数据发送给服务器并等待服务器返回数据 while(fgets(buf,sizeof(buf),stdin) != NULL) { addr_len = sizeof(srv_addr); //一但调用connect,就可以使用send函数 ret = send(sock_fd, buf, strlen(buf), 0); //如果 connect 已经指定了对方的地址。 //sendto可以这样写 sendto(sock, sendbuf, strlen(sendbuf), 0, NULL, 0); //ret = sendto(sock_fd, buf, strlen(buf), 0, NULL, 0); //sendto第一次发送的时候,会绑定地址,需要指定对方的地址 //ret = sendto(sock_fd,buf,strlen(buf),0,(struct sockaddr*)&srv_addr,sizeof(srv_addr));//开始发送 if(ret == -1) { perror("sendto"); exit(-1); } ret = recvfrom(sock_fd,buf,sizeof(buf),0,(struct sockaddr*)&srv_addr,&addr_len);//等待服务器反馈数据 if(ret == -1) { if (errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("recv from IP:%s,IP:%d\n",inet_ntoa(srv_addr.sin_addr),ntohs(srv_addr.sin_port)); fputs(buf,stdout);//打印接收到的数据 memset(buf,0,sizeof(buf));//清空缓冲区 } return 0;}
关于UDP不是基于字节流且缓冲区太小导致丢包的测试代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#define SRV_PORT 8001 int main(){ struct sockaddr_in srv_addr; int sock_fd; int ret = 0; int i= 0; char rcv_buf[1] = {0}; sock_fd = socket(AF_INET,SOCK_DGRAM,0); if(-1 == sock_fd) { perror("socket"); exit(-1); } bzero(&srv_addr,sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(sock_fd,(struct sockaddr *)&srv_addr,sizeof(srv_addr)); sendto(sock_fd, "ABCD", 4, 0, (struct sockaddr*)&srv_addr, sizeof(srv_addr));//自己给自己发送四个字节“ABCD” for(i = 0;i < 4;i++)// { //由于给定的缓冲区是1个字节--recvfrom指定的缓冲区大小--第三个参数 //如果数据包的实际大小大于recvfrom的第三个参数,则指定大小后面的数据将会被丢弃 //虽然发送到个数据包有4个字节,但是第三个参数指定的缓冲区大小只是一个字节,所以后面三个字节被内核抛弃--协议栈缓冲区已经没有数据可读 ret = recvfrom(sock_fd,rcv_buf,sizeof(rcv_buf),0,NULL,NULL);//在第二次循环的时候阻塞--因为没有数据可读了 if(-1 == ret) { if(errno == EINTR) continue; perror("recvfrom"); exit(-1); } printf("ret = %d\tbuf:%c\n",ret,rcv_buf[0]);//只能打印第一个字节 } close(sock_fd); return 0;}
- 网络编程中的UDP通信
- Java中的TCP/UDP网络通信编程
- Java中的TCP/UDP网络通信编程
- 网络编程--UDP通信
- 网络编程-udp通信
- java中的网络通信编程之UDP篇
- Java网络编程中的TCP,UDP通信方式详解
- 网络通信编程demo1[Udp]
- Java网络编程:UDP通信
- Java网络编程:UDP通信
- JAVA网络编程--UDP通信
- Java网络编程:UDP通信
- JAVA 网络编程 UDP通信
- 网络编程2----UDP通信
- Java网络编程之UDP网络通信
- C# 网络编程实例 UDP TCP通信
- JAVA网络编程之UDP通信演示
- UNIX 网络编程测试代码 -- UDP通信
- makefile 和shell文件相互调用
- 谁让APP工程师产生了泡沫?
- 在git@osc上有两个码云账号,如何配置互不干扰?
- LeetCode84 --Largest Rectangle in Histogram
- contentSize、contentOffset、contentInset
- 网络编程中的UDP通信
- loadrunner关联技术的一个示例
- Path expected for join!错误处理
- appstore审核的那些事 --1. 卡死在:正在通过 iTunes Store进行鉴定
- 用dbschema工具导出Informix数据库的结构的具体步骤:
- 结构体/枚举/共用体
- SVN:cleanup failed to process the following paths 错误的解决方案
- 2016年个人总结
- Linux下查看内存的使用情况