!!!!!!!!!!!!
分类: LINUX
2009-08-12 15:33:18
【摘要】
本文主要说明了一些关于UDP的基本套接口函数。
【关键词】
网络编程
一、问题的提出有关UDP的套接口编程函数了解的比较浅,需要详细了解一下。
二、解决思路1)int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen)
recv()用来接口收远程主机经指定socket传来的数据,并把数据存到由参数buf指向的内存空间,参数len为接收数据的最大长度。参数flags一般设0。参数from用来指定欲传送的网络地址。参数fromlen为sockaddr的结构的长度,它是值-结果参数。
成功则返回接收到的字符数,失败返回-1,错误原因存于errno中。
如果from参数是一个空指针,那么相应的长度参数fromlen也必须是一个空指针,表示我们并不关心数据发送者的协议地址。
2)int sendto(int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)
sendto()用来将数据由指定的socket传给对方主机。参数s为已建立好连接的socket,如果利用udp协议则不需经过连接操作。参数msg指向欲连线的数据内容,参数len则为数据长度。参数flags一般设0。参数to用来指定欲传送的网络地址。参数tolen为sockaddr的结果长度。
成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno中。
下面客户-服务器程序中的客户端程序中的一部分。
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE+1];
socklen_t len;
struct sockaddr *preply_addr;
preply_addr = Malloc(servlen);
while(Fgets(sendline, MAXLINE, fp) != NULL)
{
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
len = servlen;
n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, len);
if(len!=servlen || memcmp(pservaddr, preply_addr, len)!=0)
{
printf("reply form %s (ignored)\n", sock_ntop(preply_addr, len));
contiue;
}
recvline[n] = 0;
Fputs(recvline, stdout);
}
}
如果服务器运行在一个单个IP地址的主机上,那么这个新版本的客户工作正常。然而如果服务器主机是多宿的,该客户就有可能失败。
大多数IP实现接受宿地址为本主机任一IP地址的数据报,而不管数据报到达的接口。这样成之为弱端系统模型。如果一个系统实现的是强端系统模型,那么它将只接受到达接口和宿地址一致的数据报。
Recvfrom返回的ip地址(UDP数据报的源ip地址)不是我们所发送数据报的宿ip地址。当服务器发送应答时,宿ip地址是172.24.37.94。主机内核中的路由功能为之选择172.24.37.94作为外出接口。既然服务器没有在其套接口上绑定一个实际的ip地址,因此内核将为封装这些应答的ip数据报选择源地址。选为源地址的是外出接口的主ip地址。还有,既然它是外出接口的主ip地址,如果我们指定发送数据报到该接口的某个非主ip地址,将导致上面的程序的失败。
一个解决办法是:得到有recvfrom返回的ip地址后,客户通过dns中查找服务器主机的名字来验证该主机的域名而不是它的ip地址。另外一个解决办法:udp服务器给服务器主机上配置的每个IP地址创建一个套接口,bind每个ip地址到各自的套接口,然后在所有这些套接口上使用select,再从可读的套接口给出应答。既然用于给出应答的套接口上绑定的ip地址就是客户请求的宿ip地址,这就保证应答的源地址与请求的宿地址相同。
客户的临时端口是在第一次调用sendto时一次性选定的,不能改变;然而客户的ip地址却可以随客户发送的每个udp数据报而变动(假设客户没有捆绑一个具体的ip地址到其套接口上)。如果客户捆绑了一个ip地址到其套接口上,但是内核决定外出数据报必须从另一个数据链路发出,这种情况下,ip数据报将包含一个不同于外出链路ip地址的源ip地址。
3)已连接udp套接口
对于已连接udp套接口(调用过connect),与缺省的未连接udp套接口相比,发生了三个变化:
1.不能给输出操作指定宿ip地址和套接口。也就是说,不能使用sendto(),而改写write或send。写到已连接udp套接口上的任何内容都自动发送到connect指定的协议地址。
2.不能使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。在一个已连接udp套接口上由内核为输入操作返回的数据报仅仅是那些来自connect所指定协议地址的数据报。目的地为这个已连接udp套接口的本地协议,发源地却不是该套接口早先connect到的协议地址的数据报,不会投递到该套接口。这样就限制一个已连接udp套接口能且仅能与一个对端交换数据报。
3.由已连接udp套接口引发的异步错误返回给它们所在的进程。相反,未连接udp套接口不接受任何异步错误。
拥有一个已连接udp套接口的进程可为下列两个目的之一再次调用connect:
A.指定新的ip地址和端口号
B.断开套接口。
为了断开一个已连接udp套接口,再次调用connect时把套接口地址结构的地址族成员设置为AF_UNSPEC。这么做可能返回一个EAFNOSUPPORT错误,不过没关系。使得套接口断开连接的是在已连接udp套接口上调用connect的进程。
已连接udp套接口还可用来确定用于某个特定目的地的外出接口。这是有connect函数应用到udp套接口时的一个副作用造成的:内核选择本地ip地址(假设其进程未曾调用bind显式指派它)。这个本地ip地址通过为宿ip地址搜索路由表得到外出接口,然后选用该接口的主ip地址而选定。在udp套接口上调用connect并不给对端主机发送任何信息,它完全是一个本地操作,只是保存对端的ip地址和端口号。在一个未绑定端口号的udp套接口上调用connect同时也给该套接口指派一个临时端口。
三、总结主要介绍了基本udp网络编程。
四、效果评价希望对udp编程有些帮助。