!!!!!!!!!!!!
分类: LINUX
2009-08-03 19:22:05
【摘要】
本文主要说明了一些关于TCP的基本套接口函数。
【关键词】
网络编程
一、问题的提出有关tcp的套接口编程函数了解的比较浅,需要详细了解一下。
二、解决思路主要介绍socket()、connect()、bind()、listen()、listen()、accept()、 close()、getsockname()、getpeername()。
1) socket函数
int socket(int family, int type, int protocol)
其中family参数指明协议族,type参数指明套接口类型,protocol为协议类型。
Family的取值为:
AF_INET: ipv4协议
AF_INET6:IPV6协议
AF_LOCAL:unix域协议
AF_ROUTE:路由套接口
AF_KEY:密钥套接口
Type的取值为:
SOCK_STREAM:字节流套接口
SOCK_DGRAM:数据报套接口
SOCK_SEQPACKET:有序分组套接口
SOCK_RAW:原始套接口
Protocol的取值为:
IPPROTO_TCP:tcp传输协议
IPPROTO_UDP:UDP传输协议
IPPROTO_SCTP:stcp传输协议
2)connect函数
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
Sockfd是由socket函数返回的套接口描述字,第二个、三个参数分别是一个指向套接口地址结构的指针和该结构的大小。
客户在调用函数connet前不必非得调用bind函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。
如果是tcp套接口,调用connect函数将激发tcp的三路握手过程,而且仅在连接建立成功或出错时才返回,其中出错的返回的可能有以下几种情况。
1.若tcp客户没有收到SYN分节的响应,则返回ETIMEDOUT错误。
2.若对客户的SYN的响应的响应是RST(表示复位),则表明该服务器主机在我们指定的端口上没有进程在等待与之连接。返回的错误为ECNNREFUSED。产生RST的三个条件是:目的地为某端口的SYN到达,然后该端口上没有正在监听的服务器;tcp想取消一个已有连接;tcp接收到一个根本不存在的连接上的分节。
3.若客户发出的SYN在中间的某个路由器上引发一个“destination unreachable”ICMP错误,则认为是一种软错。返回错误为EHOSTUNREACH或ENETUNREACH。
若connect失败则该套接口不再可用,必须关闭,不能对这样的套接口再次调用connect函数。
3)bind函数
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen)
sockfd为socket函数返回的套接口描述字,myaddr指向特定于协议的地址结构的指针,addrlen为地址结构的长度。
对于tcp,调用bind函数可以指定一个套接口,指定一个ip地址,可以两者都指定,也可以都不指定。
1.服务器在启动时捆绑它们的众所周知端口。如果一个tcp客户或服务器未曾调用bind捆绑一个端口,当调用connect或listen时,内核就要为响应的套接口选择一个临时端口。让内核来选择临时端口对于tcp客户来说是正常的,除非应用需要一个预留端口;然而对于tcp服务器来说却极为罕见,因为服务器是通过它们众所周知端口被大家认识的。
2.进程可以把一个特定的ip地址捆绑到它的套接口上,不过这个ip地址必须属于其所在主机的网口接口之一。对于tcp客户,这就为在该套接口上发送的ip数据报指派了源ip地址。对于tcp服务器,这就限定该套接口只接收那些目的地为这个ip地址的客户连接。
3.tcp客户通常不把ip地址捆绑到它的套接口上。当连接套接口时,内核根据所用外出网络接口来选择源ip地址,而所用外出接口则取决于到达服务器所需的路径。
4.如果tcp服务器没有把ip地址捆绑到它的套接口上,内核就将客户发送的SYN的宿ip地址作为服务器的源ip地址。
5.如果指定端口号为0,那么内核就在bind被调用时选择一个临时端口;然而如果指定ip地址为通配地址,那么内核将等到套接口已连接(tcp)或套接口上发送数据(udp)时才选择一个本地ip地址。
4)listen函数
int listen(int sockfd, int backlog)
sockfd为socket函数范围的套接口描述字, backlog限定了内核应该为响应套接口排队的最大连接个数。通常应该在调用socket和bind这两个函数之后,并在调用accept函数之前调用。
5)accept函数
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)
accept有tcp服务器调用,用于从已完成连接队列对头返回下一个已完成连接。参数cliaddr和addrlen用来返回已连接的对端进程的协议地址。Sockfd为监听套接口描述字,accept返回的是已连接套接口描述字。
一个服务器通常仅仅创建一个监听套接口,它在该服务器的生命期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接口。当服务器完成对于某个给定客户的服务时,相应的已连接套接口就被关闭。
6)close函数
int close(int sockfd)
close一个tcp套接口的缺省行为是把套接口标记成已关闭,然后立即返回到调用进程。该套接口描述字不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数。然而tcp将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的tcp连接终止序列。
如果父进程对每个由accept返回的已连接套接口都不调用close,那么并发服务器中将会发生什么。首先,父进程最终将耗尽可用描述字,因为任何进程在任何时刻可拥有的打开着的描述字数通常是有限制的。不过更重要的是,没有一个客户连接会被终止。当子进程关闭已连接套接口时,它的引用计数值将由2递减为1且保持为1,因为父进程永不关闭任何已连接套接口。这将妨碍tcp连接终止序列的发生,导致连接一直打开着。
7)getsockname和getpeername函数
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen)
返回与某个套接口关联的本地协议地址。
int getpeername(int sockfd, struct sockaddr *peeradd, socklen_t *addrlen)
返回与某个套接口关联的远地协议地址。
1.在一个没有调用bind的tcp客户上,connect成功返回后,getsockname用于返回由内核赋予该连接的本地ip地址和本地端口号。
2.在以端口号0调用bind后,getsockname用于返回由内核赋予的本地端口号。
3.getsockname可用于获取某个套接口的地址族。
4.在一个以通配ip地址调用bind之后的tcp服务器上,与某个客户的连接一旦建立(accept成功返回),getsockname就可以用于返回由内核赋予该连接的本地ip地址。在这样的调用中,套接口描述字参数必须是已连接套接口的描述字,而不是监听套接口的描述字。
5.当一个服务器是由调用过accept的某个进程通过调用exec更换了程序时,它能够获取客户身份的唯一途径便是调用getpeername。