《udp概述及tcp缓冲区》

一、udp协议概述

1、UDP(user datagram protocol)的中文叫用户数据报协议,属于传输层。UDP是面向非连接的协议,它不与对方建立连接,而是直接把我要发的数据报发给对方。所以UDP适用于一次传输数据量很少、对可靠性要求不高的或对实时性要求高的应用场景。正因为UDP无需建立类如三次握手的连接,而使得通信效率很高。

2、使用UDP编写的一些常用应用程序有:DNS(域名系统)、NFS(网络文件系统)和SNMP(简单网络管理协议)

3、事实上,任何被epoll侦听的文件符只要其被关闭,那么它也会自动从被侦听的文件描述符集合中删除,很是智能。
每次添加/修改/删除被侦听文件描述符都需要调用epoll_ctl,所以要尽量少地调用epoll_ctl,
防止其所引来的开销抵消其带来的好处。有的时候,应用中可能存在大量的短连接(比如说Web服务器),
epoll_ctl将被频繁地调用,可能成为这个系统的瓶颈。

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); 
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);
//均返回:若成功则为读或写的字节数,出错为-1

4、写一个长度为0的数据报是可行的,这也意味着对于数据报协议,recvfrom返回0值也是可行的;它不表示对方已经关闭了连接,这与TCP套接口上的read返回0的情况不同。由于UDP是无连接的,这就没有诸如关闭UDP连接之类的事情。

5.UDP的connect函数
我们可以给UDP套接口调用connect,但这样做的结果却与TCP连接毫不相同:没有三路握手过程。内核只是记录对方的IP地址和端口号,它们包含在传递给connect的套接口地址结构中,并立即返回给调用进程。

对于已连接UDP套接口,与缺省的未连接套接口相比,发生了三个变化:

1).我们再也不能给输出操作指定宿IP和端口号,也就是说我们不使用sendto,而改用write或send,写到已连接UDP套接口上的任何内容都自动发送到由connect指定的协议地址(例如IP地址和端口号)
2).我们不必使用recvfrom以获悉数据报的发送者,而改用read,recv或recvmsg,在一个已连接UDP套接口上由内核为输入操作返回的数据报仅仅是那些来自connect所指定协议地址的数据报。目的地为这个已连接UDP套接口的本地协议地址,发源地却不是该套接口早先connect到的协议地址的数据报,不会投递到该套接口。这样就限制了一个已连接UDP套接口而且仅能与一个对端交换数据报。
3).由已连接的UDP套接口引发的异步错误返回给他们所在的进程,而未连接UDP套接字不接受任何异步错误。

【udp高并发实现方式】
一、tcp并发与udp并发的区别
       无论是epoll还是select,在观察有无数据就绪时,都是针对多个文件描述符。如果只有一个文件描述符,那么进程只要观察那一个文件描述符即可。在网络编程中,一个Socket对应一个文件描述符。Tcp协议的server在监听端口前初始化一个socket,每有一个新的连接,就新建一个socket。因此当tcp服务器面对高并发请求时,实际上有多个socket,也就是有多个文件描述符。Udp协议的Server没有真正意义上的“连接”的概念,在监听端口和响应请求时都只有一个socket,也就只有一个文件描述符。

二、udp并发的常规思路
       大部分udp服务器是顺序迭代的,服务器等待客户端请求,然后读取请求,处理请求,发回响应。但是,当处理客户端请求需要很长时间,就需要考虑某种形式的并发。一个“长处理”可以理解为处理请求的时间明显大于发送请求的时间。
      并发的常见思路是使用多线程。服务器在读取一个新请求之后,可以交由一个线程处理,该线程在处理之后直接将响应内容发给客户端。另一方面,udp服务器和多个客户端交互,但是却没有多个socket。典型的解决方案是,服务器为每个客户端创建一个新的socket,并绑定一个新的端口。客户端以后就通过这个新的socket与服务器通信,获得响应。总结来说,udp并发服务器,针对多个客户端,可以创建多个socket;针对多个请求,可以使用多线程(线程池)进行处理。


三、有关TCP和UDP 粘包消息保护边界

在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的。
TCP的socket编程,收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。
 
对于UDP,不会使用块的合并优化算法,这样,实际上目前认为,是由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。

保护消息边界,就是指传输协议把数据当作一条独立的消息在网上  传输,接收端只能接收独立的消息.也就是说存在保护消息边界,接收  端一次只能接收发送端发出的一个数据包.  

而面向流则是指无保护消息保护边界的,如果发送端连续发送数据,  接收端有可能在一次接收动作中,会接收两个或者更多的数据包. 我们举个例子来说,例如,我们连续发送三个数据包,大小分别是2k, 
 4k , 8k,这三个数据包,都已经到达了接收端的网络堆栈中,如果使  用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有  三次接收动作,才能够把所有的数据包接收完.而使用TCP协议,我们  只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的  数据包接收下来.只需要有一次接收动作. 

 【INADDR_ANY】
 代表任意地址,假设一个主机有inter1,inter2,,inter3三个接口,如果一个socket绑定了INADDR_ANY的地址和8000的端口,那么,从客户端过来的一个UDP包到达该主机,不管客户端connect的是inter1,inter2,inter3中的哪个地址,都会被该socket接收到。如果此时主机还要再建立一个新的socket,使用inter1接口和端口8000,将会失败,因为这个端口和地址已经被第一个socket监听了。

上面是接收的情况,那么,发送数据报给客户端的时候呢,到底是用哪个接口发送呢? 
这个就是根据本机路由表的配置情况,选择最合适的路径对应的接口来发送。

【socket 发送和接受缓冲区】

一、Socket缓冲区大小修改与系统设置
1、每个Socket在Linux中都映射为一个文件,并与内核中两个缓冲区(读缓冲区、写缓冲区)相关联。或者说,每个Socket拥有两个内核缓冲区。
有时,我们需要修改缓冲区的内核限制的最大值,使其符合我们的实际需求
2、系统设置
cat /proc/sys/net/core/rmem_max
cat /proc/sys/net/core/wmem_max
cat /proc/sys/net/core/rmem_default
cat /proc/sys/net/core/wmem_default

注:
rmem_max:一个Socket的读缓冲区可由程序设置的最大值,单位字节; 
wmem_max:一个Socket的写缓冲区可由程序设置的最大值,单位字节; 
rmem_default:一个Socket的被创建出来时,默认的读缓冲区大小,单位字节; 
wmem_default:一个Socket的被创建出来时,默认的写缓冲区大小,单位字节;

注:
/proc是一个很特殊的文件系统,其并非真实存在于物理磁盘,而是当前系统运行状态的一个映射,存在于RAM中。

3、应用程序级修改缓冲区大小
我们可以在程序中动态地修改(通过setsockopt系统调用)持有的有效Socket的读写缓冲区大小。
我们通过setsockopt系统调用成功地修改了sock的接收缓冲区大小。但是,代码级的修改缓冲区大小,不是万能的,其受限于系统配置。

4、系统配置级修改缓冲区大小
 su - root
 echo 262144 > /proc/sys/net/core/rmem_default
 echo 1048576 > /proc/sys/net/core/rmem_max
5、setsockopt系统调用级设置受限于系统运行时配置,可以通过修改系统配置,使得程序设置更大的读写缓冲区。
需要注意的两点:
1.)当系统关机重启时,对/proc的修改,是否依然存在?
不会。
这就比较重要,若服务器由于异常宕机,重启后失去了原有的设置,就有可能导致接收缓冲区过小,出现UDP丢包的可能。

2.)为什么我通过setsockopt设置读缓冲区值为rcvBufSize,但实际getsockopt获取的读缓冲区大小是2*rcvBufSize?
这个是和源码有关系。

二、缓冲区查看
1. TCP收发缓冲区默认值 
[root@qljt core]# cat /proc/sys/net/ipv4/tcp_rmem 
4096 87380 4161536(TCP接收缓冲区min,default,max)
cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4161536(TCP发送缓冲区min,default,max)

2. UDP收发缓冲区默认值
[root@qljt core]# cat /proc/sys/net/core/rmem_default
1048576(UDP接收缓冲区的默认值1M)

[root@qljt core]# cat /proc/sys/net/core/wmem_default
1048576(UDP发送缓冲区的默认值1M)

3. TCP或UDP收发缓冲区最大值
[root@qljt core]# cat /proc/sys/net/core/rmem_max 
8388608(TCP或UDP接收缓冲区的最大值8M)
[root@qljt core]# cat /proc/sys/net/core/wmem_max
8388608(TCP或UDP发送缓冲区的最大值8M)

可以通过setsockopt()和getsockopt()函数设置和获取相应缓冲区的大小

三、UDP的包长度
1、dp数据包的包头可以看出,udp的最大包长度是2^16-1的个字节。由于udp包头占8个字节,而在ip层进行封装后的ip包头占去20字节,所以这个是udp数据包的最大理论长度是2^16-1-8-20=65507。
2、首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层、网络层、运输层、应用层。UDP属于运输层,在传输过程中,udp包的整体是作为下层协议的数据字段进行传输的,它的长度大小受到下层ip层和数据链路层协议的制约。
3、以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的。这个1500字节被称为链路层的MTU(最大传输单元)。因特网协议允许IP分片,这样就可以将数据包分成足够小的片段以通过那些最大传输单元小于该数据包原始大小的链路了。这一分片过程发生在网络层,它使用的是将分组发送到链路上的网络接口的最大传输单元的值
4、由于网络接口卡的制约,mtu的长度被限制在1500字节,这个长度指的是链路层的数据区。对于大于这个数值的分组可能被分片,否则无法发送,而分组交换的网络是不可靠的,存在着丢包。IP 协议的发送方不做重传。接收方只有在收到全部的分片后才能 reassemble并送至上层协议处理代码,否则在应用程序看来这些分组已经被丢弃。
5、因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节。而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的。又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节。这个1472字节就是我们可以使用的字节数。

假定同一时刻网络丢包的概率是均等的,那么较大的IP datagram必然有更大的概率被丢弃,因为只要丢失了一个fragment,就导致整个IP datagram接收不到。不超过MTU的分组是不存在分片问题的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值