unix网络编程笔记doc.docx

上传人:b****5 文档编号:6085638 上传时间:2023-01-03 格式:DOCX 页数:9 大小:26.03KB
下载 相关 举报
unix网络编程笔记doc.docx_第1页
第1页 / 共9页
unix网络编程笔记doc.docx_第2页
第2页 / 共9页
unix网络编程笔记doc.docx_第3页
第3页 / 共9页
unix网络编程笔记doc.docx_第4页
第4页 / 共9页
unix网络编程笔记doc.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

unix网络编程笔记doc.docx

《unix网络编程笔记doc.docx》由会员分享,可在线阅读,更多相关《unix网络编程笔记doc.docx(9页珍藏版)》请在冰豆网上搜索。

unix网络编程笔记doc.docx

unix网络编程笔记doc

unix网络编程笔记

unix网络编程笔记2010-06-0416:

09索引:

1处理SIGCHLD信号2捕获信号时,注意处理被中断的系统调用3accept返回前连接夭折的处理4具有多个输入的处理5SIGPIPE的产生和处理6处理服务器主机崩溃7处理服务器主机崩溃重启8处理服务器主机关机9网络函数的可重入问题10套接口设置超时的方法11辅助数据12如何得知套接口接收队列中有多少数据?

13UNIX域协议14UNIX域套接口使用套接口函数的一些差别和限制15描述字传递机制16非阻塞套接口I/O17服务器程序常见设计方法18注意网络编程的移植性问题19注意对等方的不合理行为20开发和使用应用程序"框架"21在应用程序中中实现keep-alive机制22理解TCP的写操作23使应用程序事件驱动24不要试图绕过TIME-WAIT状态25服务器应当设置SO_REUSEADDR选项26尽量使用大型写操作代替多个小规模写操作27注意异步connect的可移植问题28避免数据拷贝在使用之前置sockaddr_in结构为029理解缓冲区大小对TCP性能的影响30理解已连接UDP套接字

--

1.处理SIGCHLD信号当编写fork子进程处理连接的服务器程序时,子进程退出会给父进程产生SIGCHLD信号,父进程若不处理该信号会导致僵尸进程。

处理SIGCHLD信号,使用waitpid调用,不能使用wait简单处理。

一般的处理方法如下(信号处理函数):

voidsig_chld(intsigno){pid_tpid;intstat;while((pid=waitpid(-1,&stat,WNOHANG))0)continue;return;}2.捕获信号时,注意处理被中断的系统调用信号处理可能会中断慢系统调用,所以我们必须对慢系统调用返回EINTR错误做准备。

一般处理方法如下(以accept为例):

for(;;){clilen=sizeof(cliaddr);if((connfd=accept(listenfd,(SA*)&cliaddr,&clilen)0){if(errno==EINTR)continue;elseerr_sys("accepterror");}此法对accept、read、write、select、open等合适;但connect调用不能重启,若connect返回EINTR,我们不能再调用它,否则立即返回错误。

3.accept返回前连接夭折的处理连接在listen后建立,在accept前夭折,此时accept可能会返回错误,对此必须处理。

但对于夭折的连接处理依赖于实现,源自Berkekey的实现在内核中处理夭折的连接,服务器进程根本看不到,若当前没有新连接,则accept阻塞;大多数SVR4实现会返回一个错误给进程,作为accept的返回,此值可能是EPROTO("协议错")errno值;Posix.1g要求返回ECONNABORTED("软件引起的连接夭折")。

所以,在ECONNABORTED错误情况下,服务器可以忽略错误而简单的再调用accept一次。

经过测试,在Solaris8和AIX4.3下返回ECONNABORTED,但在SCOOpenserver5.05下返回EINVAL。

4.具有多个输入的处理当工作在有两个或多个描述字的情况下,不能只阻塞于某个特定源输入中,而是应该阻塞于任一个源的输入中。

否则在网络描述字中会出现有异常数据而没有读到,导致出错的情况。

方法是使用select或poll。

5.SIGPIPE的产生和处理在一个服务器进程终止的情况下,终止关闭了描述字,此时客户对此描述字写,服务器TCP接收到来自客户的数据,由于先前打开那个套接口的进程已经终止,所以以RST响应。

客户端可能会收到先前关闭时的FIN,也可能收到后面的RST,这依赖于当时的具体情况。

因为同时有FIN和RST时,RST优先于FIN。

当进程向接收了RST的套接口进行写操作时,内核个该进程发一个SIGPIPE信号,此信号的缺省行为是终止进程,所以,进程必须捕获该信号以免不情愿的被终止。

进程不论是捕获了该信号并从其信号处理程序返回,还是不理会该信号,写操作都会返回EPIPE错误。

写一个已接收FIN的套接口是可行的,但写一个已接收了RST的套接口则是错误的。

处理SIGPIPE的建议方法取决于它发生时应用想做什么。

如果没有什么特殊的情况处理,可将信号处理方法简单的设置为SIG_IGN,并假设后续的写操作将捕捉EPIPE错误并终止。

若在信号处理程序中处理,要注意的是,如果使用了多个套接口,该信号的递交无法知道是哪个套接口出的错。

6.处理服务器主机崩溃假设服务器主机已经崩溃,客户此时写数据,客户TCP会持续重传数据分节,若客户的数据分节根本没有响应,则错误为ETIMEDOUT;若某些中间路由器判定服务器主机不可达,且以一个目的地不可达饿ICMP消息响应,则错误是EHOSTUNREACH或ENETUNREACH。

以上情况只有向服务器主机发送数据时,才能检测出它已经崩溃。

如果不主动发送也想检测出崩溃情况,则需要设置套接口选项SO_KEEPALIVE。

7.处理服务器主机崩溃重启当服务器主机崩溃并重启后,客户向服务器发送数据,由于服务器重启,它的TCP丢失了崩溃前的所有连接信息,所以服务器TCP对接收到的客户数据分节以RST响应。

若客户阻塞于读,则返回ECONNRESET错误。

8.处理服务器主机关机服务器关机会终止所有进程,进程退出时会关闭描述字,所以该情况的处理类似于SIGPIPE中服务器进程终止的讨论。

9.网络函数的可重入问题历史上,gethostbyname、gethostbyname2、gethostbyaddr、getservbyname和getservbyport是不可重入的。

一些支持线程的实现提供了相应的可重入版本(以_r结尾),也有些实现提供了这些函数的使用线程特定数据的可重入版本。

inet_pton和inet_ntop总是可重入的。

历史上inet_ntoa是不可重入的,但一些实现提供了使用线程特定数据的可重入版本。

getaddrinfo只有在它自己调用的是可重入函数时才是可重入的。

getnameinfo只有在它自己调用的是可重入函数时才是可重入的。

10.套接口设置超时的方法有三种方法:

调用alarm,在到达指定时间时产生SIGALRM信号,必要时结合sigsetjmp和siglongjmp进行使用。

使用select的时间机制来设置超时。

使用新的SO_RCVTIMEO和SO_SNDTIMEO套接口选项。

该法的缺点是不是所有的实现都支持它们,一旦为每个描述字设置了这个选项,并指定了超时值,那么这个超时值对该套接口上的所有读/写操作都起作用,所以只需设置一次选项。

但它们只能用于读/写操作,没法为connect设置超时。

当超时发生,函数返回EWOULDBLOCK。

11.辅助数据在sendmsg和recvmsg中可以使用msghdr结构中的msg_control和msg_controllen成员发送和接收辅助数据(ancillarydata)。

辅助数据的另一个叫法是控制信息(controlinformation)。

辅助数据的各种用法如下:

协议cmsg_levelcmsg_type说明IPv4IPPROTO_IPIP_RECVDSTADDR接收UDP数据报的目的地址IP_RECVIF接收UDP数据报的接口索引IPv6IPPROTO_IPV6IPV6_DSTOPTS指定/接收目标选项IPV6_HOPLIMIT指定/接收跳限IPV6_HOPOPTS指定/接收步跳选项IPV6_NEXTHOP指定下一跳地址IPV6_PKTINFO指定/接收分组信息IPV6_RTHDR指定/接收路由头部Unix域SOL_SOCKETSCM_RIGHTS发送/接收描述字SCM_CREDS发送/接收用户凭证

辅助数据由一个或多个辅助数据对象组成,每个对象由一个cmsghdr结构开头,该结构在sys/socket.h中定义如下:

structcmsghdr{socklen_tcmsg_len;/*lengthinbytes,includingthisstructure*/intcmsg_level;/*originatingprotocol*/intcmsg_type;/*protocol-specifictypefollowedbyunsignedcharcmsg_data*/};实际数据在cmsghdr结构后面,msg_control指向的辅助数据必须按cmsghdr结构进行对齐,所以在cmsg_type成员和实际数据之间可能有填充字节,在数据之后,下一个对象之前也可能有填充字节。

使用如下的宏屏蔽可能出现的填充字节:

#includesys/socket.h#includesys/param.hstructcmsghdr*CMSG_FIRSTHDR(structmsghdr*mhdrptr);返回:

指向第一个cmsghdr结构的指针,无辅助数据时为NULLstructcmsghdr*CMSG_NXTHDR(structmsghdr*mhdrptr,\structcmsghdr*cmsgptr);返回:

指向下一个cmsghdr结构的指针,不再有辅助数据对象时为NULLunsignedchar*CMSG_DATA(structcmsghdr*cmsgptr);返回:

指向与cmsghdr结构关联的数据的第一个字节的指针

unsignedintCMSG_LEN(unsignedintlength);返回:

给定数据量下存储在cmsg_len中的值

unsignedintCMSG_SPACE(unsignedintlength);返回:

给定数据量写一个辅助数据对象的总大小CMSG_LEN和CMSG_SPACE的区别在于,前者不将填充字节计算在内,因此等于cmsg_len的值,但后者将这些填充字节都计算在内,因此在动态申请辅助数据对象的数据空间时使用这个值。

12.如何得知套接口接收队列中有多少数据?

有三种方法:

如果在没有数据可读时还有其他事情要做,为了不阻塞在内核中,可以使用非阻塞I/O。

如果想检查数据而使数据仍留在接收队列中,可以使用MSG_PEEK标志。

如果想避免阻塞,可以把此标志和非阻塞套接口相结合,或与MSG_DONTWAIT标志结合使用。

注意的是,一个字节流套接口接收队列的数据量在两次连续的recv调用之间可能会改变,而UDP套接口不会。

一些实现支持ioctl的FIONREAD命令。

Ioctl的第三个参数是一个指向整数的指针,在该整数中返回的值是套接口接收队列中数据的字节数。

这个值是队列中字节数的总和,对UDP套接口来包括在队列中的所有数据报。

13.UNIX域协议UNIX域协议并不是一个实际的协议族,它只是在同一台主机上进行C/S通信时,使用与在不同主机上的C/S通信时相同的API的一种方法。

UNIX域提供了两种类型的套接口:

字节流套接口(与TCP类似)和数据报套接口(与UDP类似)。

使用UNIX域协议有三个原因:

在源自Berkeley的实现中,当通信双方位于同一台主机上时,UNIX域套接口的速度通常是TCP套接口的两倍。

UNIX域套接口可以用来在同一台主机上的各进程之间传递描述字。

UNIX域套接口的较新实现中可以向服务器提供客户的凭证(用户ID和组ID),这能提供附加的安全检查。

UNIX域套接口地址结构如下:

#includestructsockaddr_un{uint8_tsun_len;sa_family_tsun_family;/*AF_LOCAL*/charsun_path[104];/*null_terminatedpathname*/};sun_path数组中存放的路径名必须以空字符结尾。

系统提供了SUN_LEN宏,他以一个指向sockaddr_un结构的指针为参数,返回该结构的长度,长度里包括路径名中的非空字节数。

未指定地址以空字符串的路径名表示,这是UNIX域中与IPv4的INADDR_ANY常值或IPv6的IN6ADDR_ANY_INIT常值等价的一个地址。

注意:

给UNIX域套接口bind的路径名如果在文件系统中已经存在,则bind将失败。

为预防此种情况,可以先调用unlink。

14.UNIX域套接口使用套接口函数的一些差别和限制下面是Posix.1g的一些要求,并不是所有的实现都做到了这一级:

bind建立的路径名的缺省访问权限应为0777,并被当前的umask值修改;UNIX域套接口相关联的路径名应为一个绝对路径名,而不是相对路径名;connect使用的路径名必须是一个绑定在某个已打开的UNIX域套接口上的路径名,而且套接口的类型(字节流或数据报)也必须一致;用connect连接UNIX域套接口时的权限检查和用open以只写方式访问路径名时完全相同;UNIX域字节流套接口和TCP套接口类似:

他们都为进程提供一个没有记录边界的字节流接口;如果UNIX域字节流套接口的connect调用发现套接口的队列已满,会立即返回一个ECONNREFUSED错误,这和TCP有所不同;UNIX域数据报套接口和UDP套接口类似:

都提供一个保留记录边界的不可靠的数据报服务;与UDP套接口不同,在未绑定的UNIX域套接口上发送数据报不会给它捆绑一个路径名。

同样,与TCP和UDP不同的是,个UNIX域数据报套接口调用connect不会捆绑一个路径名。

15.描述字传递机制使用UNIX域套接口可以在两个没有关系的进程之间传递描述字。

4.4BSD的技术允许一次sendmsg传递多个描述字,而SVR4的技术一次只能传递一个描述字。

在两个进程之间传递描述字的步骤如下:

创建一个字节流或数据报的UNIX域套接口。

如果两进程有父子关系,可以使用socketpair,否则服务器必须创建一个UNIX域套接口,bind一个路径名,让客户connect到这个套接口。

进程可以用任何返回描述字UNIX函数打开一个描述字,如open、pipe、mkfifo、socket或accept。

可以在进程之间传递任何类型的描述字。

发送进程建立一个msghdr结构,其中包含要传递的描述字,Posix.1g说明该描述字作为辅助数据发送(msghdr结构的msg_control或msg_accright成员),发送进程调用sendmsg通过第一步得到的UNIX域套接口发出描述字。

这时该描述字是"在飞行中(inflight)",即使sendmsg之后,在recvmsg之前将该描述字关闭,它仍会为接收进程保持打开状态。

描述字的发送导致它的访问计数加1。

接收进程调用recvmsg,在UNIX域套接口上接收描述字。

通常接收进程收到的描述字的编号与发送进程中的描述字编号不同。

C/S之间必须有某种应用协议,使接收方知道何时接收描述字,如果接收方调用recvmsg但没有分配接收描述字的空间,而且一个描述字已被传递并正待读出,这个已传递的描述字就会关闭。

在用recvmsg接收描述字时要避免使用MSG_PEEK标志,否则后果不可预料。

16.非阻塞套接口I/O概述缺省状态下,套接口是阻塞方式的,当一个套接口调用不能立即完成时,进程进入睡眠状态,等待操作完成。

可能阻塞的套接口调用分为如下四种:

输入操作:

read、readv、recv、recvfrom和recvmsg函数。

在一个非阻塞套接口上,如果输入操作不能满足,它们将会立即返回一个EWOULDBLOCK错误。

输出操作:

write、writev、send、sendto和sendmsg函数。

在一个非阻塞套接口上,如果发送缓冲区没有空间,它们将会立即返回一个EWOULDBLOCK错误,如果发送缓冲区有一些空间,返回值为内核能向缓冲区中拷贝的字节数(不足计数(shortcount))。

接收外来连接:

accept函数。

在非阻塞套接口上调用accept函数,而且没有新的连接,将返回EWOULDBLOCK错误。

初始化外出的连接:

用于TCP的connect函数。

在非阻塞的套接口上调用connect,而且连接不能马上建立,连接的建立过程将开始,但返回一个EINPROGRESS错误。

也存在连接立即建立的情况。

对于非阻塞I/O不能马上完成返回的错误码不同实现有差异:

系统V返回EAGAIN,而Berkeley返回EWOULDBLOCK,幸好这两个错误码在多数实现中值相等。

非阻塞connect非阻塞connect有如下三种用途:

我们可以在三路握手的同时做一些其他的处理;可以用这种技术同时建立多个连接;我们用select等待连接的完成,因此可以给select设置一个时间限制,从而缩短connect的超时时间。

处理非阻塞connect有一些细节需要处理:

即使套接口是非阻塞的,如果连接的服务器在同一台主机上,在调用connect时连接通常会立刻建立。

源自Berkeley的实现有两条与select和非阻塞I/O相关的规则:

(1)当连接成功建立时,描述字变成可写;

(2)当连接建立出错时,描述字变成既可读又可写。

非阻塞connect有许多移植性的问题,如getsockopt等,需要注意。

被中断的阻塞connect一个被中断的阻塞connect不自动重启的情况下,它会返回EINTR,此时我们不能再调用connect等待连接建立完成,这样做将返回EADDRINUSE错误。

在这种情况下要做的是要么关闭套接口,重新调用socket和connect;要么调用select,和非阻塞connect一样处理,使select在连接建立成功(使套接口可写)或连接失败(使套接口科可读又可写)时返回。

非阻塞accept如前"accept返回前连接夭折"所述,在服务器调用accept之前客户如果放弃这个连接,源自Berkeley的实现不对服务器返回这个夭折的连接,而其他实现应返回ECONNABORTED错误,但一般返回EPROTO错误。

如果在accept之前使用select来测试连接是否准备好,要注意的是在select和accept之间如果连接夭折,源自Berkeley的实现会导致accept阻塞,直到其他某个客户建立一个连接为止。

所以使用select并不能保证accept不会阻塞。

解决方法是:

如果用select来获取何时有连接已就绪可以accept时,总是把监听套接口设置为非阻塞的;并且在后面的accept中忽略以下错误:

EWOULDBLOCK(源自Berkeley的实现在客户放弃连接时出现的错误)、ECONNABORTED(Posix.1g的实现在客户放弃连接时出现的错误)、EPROTO(SVR4的实现在客户放弃连接时出现的错误)和EINTR(如果信号被捕获)。

17.服务器程序常见设计方法服务器程序设计方法列出如下9种:

迭代服务器(无进程控制);简单并发服务器,每个客户fork一次;预先派生子进程,每个子进程相互独立调用accept;预先派生子进程,使用文件上锁保护accept;预先派生子进程,使用线程互斥锁上锁保护accept;预先派生子进程,父进程向子进程传递套接口描述字;并发服务器,每个客户请求创建一个线程;预先创建线程服务器,使用互斥锁上锁保护accept;预先创建线程服务器,由主线程调用accept。

有如下结论:

当系统负载较轻时,传统的并发服务器程序模型就够了。

相对于传统的每个客户一次fork设计,预先创建一个进程池或线程池可以减少进程控制CPU时间,大约可减少10倍以上。

某些实现允许多个子进程或线程阻塞在accept上,然而在另一些实现中,我们必须使用文件锁、线程互斥锁或其他类型的锁来确保每次只有一个子进程或线程在accept。

一般来将,所有子进程或线程都调用accept要比父进程或主线程调用accept后将描述字传递个子进程或线程来得快且简单。

由于select冲突的存在,让所有子进程或线程阻塞在同一监听套接口的accept调用上要比让它们阻塞在select调用上更为可取。

使用线程通常比使用进程快。

不过选择每个客户一个子进程还是每个客户一个线程取决于操作系统提供什么以及需什么其他程序来服务各个客户。

18.注意网络编程的移植性问题在winsock中套接口描述符用SOCKET而不是int类型定义。

尽量避免在套接口字上使用read和write函数,因为在WIN32中不支持它们。

我们一般用recv、recvfrom和recvmsg来表示"读",用send、sendto和sendmsg表示"写"。

19.注意对等方的不合理行为软件应编写成能够处理任何想象的到的错误,不管该错误可能发生的概率是如何的小。

在网络编程中,应当牢记的规则是:

不能假设对等方会严格遵守应用程序协议,甚至是在我们实现双方协议的时候也是这样。

如在长链中检查客户端是否终止,检查输入的有效性,注意缓冲区的溢出和指针失控。

20.开发和使用应用程序"框架"大多数的TCP/IP应用程序属于下面四种之一:

TCP服务器、TCP客户端、UDP服务器、UDP客户端。

每类应用程序都有类似的代码来完成初始化工作,所以可以定义一些程序框架(模板)。

使用时根据情况稍做修改即可。

21.在应用程序中中实现keep-alive机制当应用程序启用TCP的keep-alive机制时,TCP就会在连接已经空闲了一定时间间隔后发送一个特殊的段给对等方。

如果对等方主机可以到达而且对等方应用程序依然运行,对等方TCP就会响应一个ACK应答,此时通讯正常,TCP将发送keep-alive空闲时间重置为0,应用程序不会接收到消息交换的任何通知。

如果对等方主机可以到达,但对等方应用程序没有运行,则对等方TCP响应RST,发送方的TCP撤消连接并返回ECONNRESET错误给应用程序。

如果对等方主机没有响应ACK或RST消息,发送方TCP继续发送keep-alive探询消息,直到它认为对等方不可达到或已经崩溃。

此时撤消连接并通知应用ETIMEDOUT错误。

如果路由器已经返回主机或网络不可到达的ICMP消息的话,则返回EHOSTUNREACH或ENETUNREACH错误。

TCP的keep-alive机制的问题是检测的空闲时间太长(至少是2小时),而且它不仅检测死连接,同时也撤消它们,有时这并不是应用程序所期望的。

可以在应用程序中实现类似的机制来解决keep-alive的问题,通过编写心博(heartbeat)函数。

22.理解TCP的写操作应用程序写操作成功并不表示数据已经发送到对等方。

写操作把数据放到发送缓冲区中,除非TCP发送缓冲区已满,否则写操作是不会阻塞的,这意味着写操作几乎总是立即返回,而且如果它返回了,也不能保证所写数据的位置。

实际上,当写操作返回时,写入的部分或全部数据可能仍旧在排队等待传输,如果此时对等方主机或应用程序崩溃的话,数据将会丢失。

因为写操作可能在数据实际发送之前就已经返回,所以当传输发生错误时,该错误通常是从下一个操作返回。

写操作返回的错误仅仅是那些在调用写操作时就已经发生的错误,除了EPIPE错误。

23.使应用程序事件驱动利用select来实现一个通用的分时机制,允许在一定时间间隔后指定一个必须发生的事件,而且使该事件在指定的时间上异步发生。

24.不要试图绕过TIME-WAIT状态TIME-WAIT状态是TCP可靠性中很重要的一部分,程序员不应该试图绕过它,虽然可以使用SO_LINGER套接口选项来取消它。

25.服务器应当设置SO_REUSEADDR选项对于TCP服务器总是应该设置SO_REUSEADDR选项,否则没法重新启动一个在TIME-WAIT状态中还存在连接的服务器。

该选项必须在bin

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 高考

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1