UDP

同步客户端和同步服务器

差别在于:
1、服务器多绑定了一个地址。
2、收发数据差异:
服务器是被动的(先recv 再 sento)
,客户端是主动的(先sendto 再 recv)。

UDP和TCP 的不同
1、C-S对应关系不同:
UDP:
a. 客户端用一个套接字可以向多个服务端发送数据,从客户端角度看是C-S是1:n的。
因为UDP不是面向连接的,不需要像TCP去connect建立连接,recv和sent函数本身就携带了地址,有一个地址就能直接收发数据(像邮件一样);----主要是这个和TCP 的client 不同
b. 服务端通过绑定一个套接字可以接受来自不同客户端的数据。
从服务器的角度看S-C 是1:n的。

———————————————————————————————————————

UDP使用流程

①创建套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
②绑定套接字(仅服务器端)
bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
③通过套接字收数据、发数据(服务端被动收发数据、客户端主动发收数据)
recvfrom(sockfd,&buf,sizeof(buf),0,(struct sockaddr*)&addr,&len);

sendto(sockfd,&buf,sizeof(buf),0,(struct sockaddr*)&addr,len);
④关闭套接字
close(sockfd);

完整demo:
参考https://www.cnblogs.com/leezheng/p/8030011.html

上述demo 的缺陷,至少有一下几点
1、因为C端recvfrom调用没有设置超时,如果数据报没有到达服务器,或者服务器的应答没有回到客户,客户将永远阻塞于dg_cli函数中的recvfrom调用。
2、如果服务器运行在一个只有单个IP地址的主机上,客户工作正常。然而如果服务器主机是多宿的,客户recvfrom返回的IP地址可能不是客服sendto发送数据报的目的IP地址。
3、如果在不启动服务器的前提下启动客户,客户sento时,服务器主机响应一个”port unreachable(端口不可达)“的ICMP消息(异步错误),不过这个ICMP消息不返回给客户进程,所以sento将成功返回。一个基本规则是:对于一个UDP套接字,由它引发的异步错误却并不返回给它,除非它已连接。
(简而言之,不面向连接的数据报,只能确保发送到网络上,不能确定发送到对端)

解决问题 1

方案一:alarm时钟函数,跳出recvfrom 的阻塞
–使用alarm函数和sigation 打断跳出阻塞的recvfrom,参考demo:https://www.cnblogs.com/dreamrun/p/4047795.html

方案二:select,超时机制,判断读事件发生再recvfrom
参考demo https://www.cnblogs.com/liang-hk/archive/2012/04/28/2475199.html

方案三:setsockopt设置socket 的recv 和send的超时时间
demo:
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
https://blog.csdn.net/yss28/article/details/54613893 没看完

**select 函数
原型 int select(int nfds, fd_set *readset, fd_set writeset,fd_set exceptset, struct tim *timeout);
timeout
有三种可能:
1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)

———————————————————————————————————————
UDP Connect()

UDP connect 的用途
1、感知异步错误(server未启动前,client 发送数据,也会感知 端口不可达);
2、发包速度更快,提高性能,根据绑定的IP过滤数据;
(建立一对一的连接,但也失去了无connect的套接字的一对多的复用的能力)
收包的时候每个套接字仅仅接收connect的对端的数据,其他的数据会过滤掉;
3、可以使用write send sendmsg 和read recv recvmsg 的接口

UDP多次connect 的用途(TCP不能对socket多次调用connect)
1、connect不同的远端地址,此时会销毁原来的连接而建立新的连接
2、connect 断开一个已UDP套接字连接,再次调用connect时把套接字地址结构的地址族设置为AF_UNSPEC

UDP connect细节
1、连接错误:客户connect服务器时,如果没有相匹配的套接字,UDP将丢弃它们并生成相应的ICMP端口不可达错误。

2、客户connect服务器后,可调用getsockname得到内核临时分配的本地IP地址和端口号。
demo:
connect(sockfd, (struct sockaddr *)pservaddr, servlen);
len = sizeof(cliaddr);
getsockname(sockfd, (struct sockaddr *)&cliaddr, &len);
printf(“local address %s:%d\n”,
inet_ntop(AF_INET, &cliaddr.sin_addr, buf, sizeof(buf)),
ntohs(cliaddr.sin_port));

———————————————————————————————————————特殊地址
1.127.0.01
1)即localhost,127.0.0.1的域名就是localhost,也可以修改localhost所指的地址。IPV6出现后,还指向ipv6 的地址 [::1]。

也是送回地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。
127.0.0.1 这个地址通常分配给 loopback 接口。loopback 是一个特殊的网络接口(可理解成虚拟网卡),用于本机中各个应用之间的网络交互。只要操作系统的网络组件是正常的,loopback 就能工作。Windows 中看不到这个接口,Linux中这个接口叫 lo。
** 整个127.*网段通常被用作 loopback 网络接口的默认地址,都可以ping通,按惯例通常设置为 127.0.0.1

2、INADDR_ANY
1)就是指定地址为0.0.0.0的地址。
2)作为接收端(server)时,bind该地址代表接收本机任意网卡(host有多个IP)收到的数据,反之不使用,则需要每个网卡的IP去bind一次(十分麻烦,还要去获取IP)。和普通IP一样,bind INADDR_ANY调用后,所有网卡的对应port再次绑定时会失败。
3)作为发送端(client),当用调用bind()函数绑定IP时使用INADDR_ANY,表明使用网卡号最低的网卡进行发送数据,即UDP数据广播。

3、本机IP
即本地的外网IP

设置套接字为非阻塞

flags = fcntl(sockfd, F_GETFL, 0); //获取套接字标志位
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置为非阻塞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值