Chinaunix首页 | 论坛 | 博客
  • 博客访问: 243205
  • 博文数量: 59
  • 博客积分: 4010
  • 博客等级: 上校
  • 技术积分: 900
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-30 20:21
文章分类

全部博文(59)

文章存档

2011年(1)

2009年(58)

我的朋友

分类: 系统运维

2009-04-07 11:01:49

服务器:socket(), bind(), recvfrom(), sendto()
客户端:socket(), sendto(), recvfrom(), close()

#include
ssize_t recvfrom( int sockfd, char* buff, size_t nbytes, int flags,
                  struct sockaddr* from, socklen_t* addrlen );
ssize_t sendto  ( int sockfd, char* buff, size_t nbytes, int flags,
                  struct sockaddr* from, socklen_t addrlen );
需要注意的一点是,recvfrom的最后一个参数是值-结果参数,而sendto是一个值参数。

我们可以用这两个函数构成最简单的回射服务器程序,见Unix网络编程205页。

一般来讲,大多数TCP服务器是并发的,而UDP服务器是迭代的,也就是单个进程就得处理所有客户。

这一程序有许多需要考虑的细节:
. recvfrom是一个可以被阻塞的函数,那么,对客户来讲,如果它所发送的数据没到达服务器端(后面我们可以了解到,虽然sendto成功返回,但是有可能数据并不到达服务器,这将产生一个ICMP错误,但是在未连接的情况下,它并不返回给客户),或者服务器端的响应没有到达客户,那么客户将一走阻塞于recvfrom函数。
我们可以设置超时来解决这一问题,不过仅仅设置超时还不能完全解决这一问题,因为超时还不能完全判定是上面的那种情况引起的超时。
. 我们知道,知道客户端口号的任何进程都可往客户发送数据,那么这些数据很可能与正常的服务器应答混杂。
一个简单的解决办法是客户端接受服务器的地址,并将其与我们所发往的服务器的地址进行比较,如果相同,则是服务器的应答。
这对只有单个IP的主机是好使的,但是,如果是多宿主机(多宿主机的路由功能会指定一个IP作为其外出接口),而刚好客户所发往的IP地址不是其路由功能所指定的外出接口,那么这种策略将失效。一个解决办法是得出由recvfrom返回的IP地址后,客户通过在DNS中查找服务器主要的名字来验证该主机的域名而不是它的IP地址。另一个解决方法是UDP服务器给服务器上的每个IP地址创建一个套接口,bind每个IP地址到各自的套接口,然后在所有这些套接口上使用select,再从可读的套接口给出应答。既然用于给出应答的套接口上绑定的IP地址就是客户请求的宿IP地址(否则该数据报不会投递到该套接口),这就保证应答的源地址与请求的宿地址相同。

前面我们提出sendto可能引起的ICMP消息不会返回给客户,现在给出解释。
该错误是由sendto引起的,但是sendto是成功返回的。UDP输出操作成功返回仅表示在接口输出队列中具有存放所导致IP数据报的空间,该ICMP错误直到后来才返回,所以称此错误为异步错误。
即使sendto成功返回,那么错误将由recvfrom产生,而recvfrom可以返回的仅有信息是errno值,它没有办法返回出错数据报的宿IP地址和宿UDP端口号,因此:
对于一个UDP套接口,由它引发的异步错误并不返回给它,除非它已连接。

UDP的connect函数
UDP的connect函数结果与TCP连接大相径庭:没有三路握手,内核只是检查是否存在立即可知的错误(比如一个明显不可达的旷目的地),记录对端的IP地址和端口号,然后立即返回调用进程。也就是说,它完全是一个本地操作。
对于已连接UDP套接口,与缺省的未连接的套接口相比,发生了三个变化:
1,不用再给输出指定IP地址和端口号。也就是说,不使用sendto而使用write或send。
2,不使用recvfrom,而改用read,recv或recvmsg。
3,由已连接UDP套接口引发的异步错误返回给它们所在的进程。

UDP客户进程或服务器进程仅仅在使用自己的UDP套接口与确定的唯一对端进行通信时,才可以调用connect。
拥有一个已连接UDP套接口的进程可以为下列两个目的之一再次调用connect:
1,指定新的IP地址和端口
2,断开套接口,此时我们将地址结构的地址族成员即sin_family或sin6_faimily设置为AF_UNSPEC,这可能会返回一个EAFNOSUPPORT的错误,不过没有关系。

性能上,在一个未连接的UDP套接口上的每个数据报调用sendto函数都会执行以下步骤:
1,连接套接口
2,输出第一个数据
3,断开套接口连接
然而如果显示连接的话,则会在connect后连接输出数据报:
1,连接套接口
2,输出第一个数据报
3,输出第二个数据报
4,...

UDP是缺乏流量控制的,实际上,所有数据主机均可以接受到,但是由于UDP套接口缓冲区有限,在套接口缓冲区满后将丢弃再收到的数据。其缺省大小为42080字节,我们可以通过使用SO_RCVBUF套接口选项来修改此值:
n = 220 * 1024;
setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n) );




阅读(1462) | 评论(0) | 转发(0) |
0

上一篇:李小龙简介

下一篇:超新星

给主人留下些什么吧!~~