UDP网络编程小总结

     因为最近在做有关网络编程的项目,用到了多播UDP通信。之前也是看了好多遍,但是总是没有形成清晰合理的架构网络。今天再次看了一遍,好记性不如烂笔头,这次总结写下博客。

客户服务器通信服务流程交互时间线如下图所示: 

按照图,依次介绍各个函数,以及我的理解。

socket函数

首先从socket函数说起,这里和TCP通信一样,通过socket函数,指定期望的通信协议类型。

#include<sys/socket.h>

Intsocket(int family,int type,int protocol);

成功返回非负描述符,若出错则返回-1.

 

第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);

第二个参数指明套接口类型,主要有种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);

Protocol参数可以如上图所示的某个协议类型常值,或者设为0,已选择所给定family和type组合的系统默认值。

Recvfrom和sendto函数

 

   其中这两个函数的前三个参数socket,buffer,nbytes意思相同,等同于read和write函数的单个参数:描述符、指向读入或者写出缓冲区的指针和读写字节数。

   Flags一般总是置位0,这里先不进行介绍,后续进行在进行说明。

   Sendto函数中的to参数,指向将要进行发送的接受者的协议地址(如IP地址和端口号)的套接字地址结构,大小是addrlen决定。

   Recvfrom的from参数则是将从何处接收的,也即是谁发送给你的发送方的协议地址套接字结构,通过from就可以知道是谁发给你的信息,你也可以通过这个获取到源地址和源端口,从而可以进行回复消息。其中from参数也可以是一个空指针,则其对应长度参数addrlen1空指针,表示我们并不进行保存发送方的信息,并不关心数据发送者的协议地址。

   注意:sendto的最后一个参数是一个整数值,而recvfrom的最后一个参数是一个指向整数值的指针(即值-结果参数)。

两个函数的共同点就是把所读写的数据的长度作为函数返回值。

一些使用情况说明:

   在使用sendto函数时,若套接字没有进行地址端口的绑定,那么内核就会为它选择一个临时端口。其实也可以跟TCP一样,进行bind调用,进行客户端的绑定,但很少做,一般客户端都不进行绑定。

   如果在一个具有多个网卡的电脑上进行测试,服务器端没有进行套接字绑定,会出现一些可能问题。当我们向服务器发送的报文目的IP是其中一个网卡,服务器收到后可能用另一个网卡进行回复,也即是sendto和recvfrom得到的ip不是一个ip。解决办法是:UDP服务器给服务器主机上配置的每个IP创建一个套接字,用bind捆绑每个IP地址到各自的套接字,然后在所有这些套接字上使用select(等待其中任何变得可读),再次可读套接字给出应答。既然用于给出应答的套接字上绑定的IP地址就是客户请求的目的地址(否则数据包不会被投递到该套接字),这就保证应答得源地址与请求的目的地址相同。

Bind函数

Bind函数是把一个协议地址赋予一个套接字。对于网际协议,协议地址是32位的IPv4地址或者128位的IPv6地址与16位的TCP或UDP端口号的组合。

 

Bind函数与名字没有任何关系。它只是把一个协议地址赋予一个套接字,至于协议地址的含义则取决于协议本身。

第二个参数myaddr是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度。对于TCP,调用bind函数可以指定一个端口号,或者一个IP地址,也可以两者都绑定,还可以都不绑定。(因为TCP是三次握手建立连接的协议,所以基本都要进行bind绑定)

因为bind在TCP中用,所以顺便也说下在TCP中的运用情况。

服务器启动时绑定他们众所周知的端口,当一个TCP客户或者服务器没有调用bind捆绑一个端口,则调用connect或listen时,内核会为相应的套接字选择一个临时端口。内核选择临时端口对于TCP客户来说是正常的,除非应用需要一个预留端口;然而对于TCP服务器来说却极为罕见,因为服务器是通过他们众所周知的端口被我们知道了解。

进程可以把一个特定的IP地址捆绑到他的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一。(个人理解:比如说,你使用动态分配IP地址进行上网,这个所分配给你的IP是无法进行地址端口套接字绑定的)。对于TCP客户,这就为在该套接字上发送的IP数据报指派了源IP地址。对于TCP服务器,这就限定该套接字只能接收那些目的地为这个IP地址的客户连接。TCP客户通常不把IP地址捆绑到它的套接字上。当连接套接字时,内核将根据所用外出网络接口来选择源IP地址,而所有外出结口则取决于到达服务器所需的路径。如果TCP服务器没把IP地址捆绑到它的套接字上,内核就把客户发送的SYN的目的IP作为服务器的源地址。前面说的,bind可以指定IP地址和端口,可一两者都指定,也可以都不指定。

通过设置sin_addr和sin_port或者sin6_addr和sin6_port的值。

如果端口号为0,那么内核就在bind调用时选择一个临时端口,如果指定地址为通配地址,那么内核就等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)才选择一个本地IP地址。

对于IPv4来说,通配地址由常值INADDR_ANY来制定,其值一般为0.告知内核去选择IP地址。

Structsockaddr_in servaddr;

Servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

对于IPv6来说:

Structsockaddr_in6  serv;

Servaddr.sin6_addr.s_addr=in6addr_any;

  如果让内核为套接字选择一个临时端口号,那么必须注意,函数bind并不返回所选择的值。为了得到内核所选择的这个临时端口值,必须调用函数getsockname来返回协议地址。

从客户角度总结UDP通信客户/服务器

  客户必须给sendto调用指定服务器的IP地址和端口号。一般来说,客户的地址和端口号都由内核自动选择,当然也可以通过bind进行指定,当有内核进行选择时,客户的临时端口是在第一次调用sendto时一次性选定,不能改变;然而客户的IP地址却可以随客户发送的每个UDP数据报而变动(假设客户没有捆绑一个具体的IP地址到其套接字上)。

从服务器角度总结UDP通信客户/服务器

   服务器想从到达的IP数据报上取得至少四条信息:源IP地址、目的IP地址、源端口号、目的端口号。下图给出了TCP服务器和UDP服务器返回这些信息的函数调用。

  TCP服务器总能得到这四条信息,因为TCP是建立连接的,且着四个值在整个生命周期内保持不变。然而对于UDP套接字,目的IP地址只能通过为IPv4设置IP_RECVDSTADDR套接字选项(IPv6设置IPV6_PKTINFO套接字选项)然后调用recvmsg(不是recvfrom)取得。由于UDP无连接的,因此目的地址可随发送到服务器的每个数据包而改变。UDP服务器也可接受目的地址为服务器主机的某个广播地址或多播地址的数据报。

 

UDP的Connect函数

虽然说UDP是无连接的,用不着connect函数,但这里的connect和TCP并不一样,并不用进行三次握手过程。内核只是检查是否存在立即可知的错误(例如一个显然不可达的目的地),记录对端的IP地址和端口号(取自传递给connect的套接字地址结构),然后立即返回到调用进程。这里来介绍一下connect函数。

Sockfd是由socket函数返回的套接字描述符,第二个第三个分别是指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。

在调用connect函数之前不必非得调用bind函数,因为内核会确定源IP地址,并选择一个临时端口作为源端口。

这样,UDP区分为:

未连接的UDP套接字:新创建的套接字默认如此。

已连接的UDP套接字:对UDP套接字调用connect的结果。

他们之间主要有三种区别:

(1) 我们不能给输出操作指定目的IP地址和端口号。也即是我们不使用sendto而改用write或send。写到已连接UDP套接字上的任何内容都自动发送到由connect制定的协议地址(如IP地址和端口号)。其实,我们也可以给已连接的UDP套接字调用sendto,但是不能指定目的地址。Sendto的第五个参数(指向目的地址的套接字地址结构的指针)必须为空指针,第六个参数(该套接字地址结构的大小)应该为0.

(2)我们不必使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。在一个已连接的UDP套接字上,由内核为输入操作返回的数据报只有那些来自connect所指定协议的数据报。目的地是这个已连接UDP套接字的本地协议地址(如IP地址和端口号),发源地却不是该套接字早先connect到的协议地址的数据报,不会投递到该套接字。这样就限制了一个已连接UDP套接字能且只能与一个对端交换数据报。确切的说,一个已连接的UDP套接字仅仅与一个IP地址交换数据报,因为connect到多播或广播地址是可能的。

(3)由已连接UDP套接字引发的异步错误会返回给它们所在的进程,而未连接UDP套接字不接受任何异步错误。


猜你喜欢

转载自blog.csdn.net/haoxiuzhao/article/details/80286686