基于TCP客户/server程序的套接字函数图例如以下:
运行网络I/O。一个进程必须做的第一件事就是调用socket函数。指定期望的通信协议类型。
#include <sys/socket.h>
int socket(int family, int type, int protocol);/*返回值:若成功则为非负描写叙述符,若出错则为-1*/
socket函数成功时返回一个小的非负整数值,它与文件描写叙述符类似。把它称为套接字描写叙述符,简称sockfd。family參数指明协议族。被称为协议域。type參数指明套接字类型。
protocol參数应该是某个协议类型常值。或者为0,以选择所给定family和type组合的系统默认值。
各參数列于一下表格:
family | 说明 | type | 说明 | protocol | 说明 |
AF_INET | IPv4协议 | SOCKET_STREAM | 字节流套接字 | IPPROTO_TCP | TCP传输协议 |
AF_INET6 |
IPv6协议 | SOCK_DGRAM | 数据报套接字 | IPPROTO_UDP | UDP传输协议 |
AF_LOCAL | Unix域协议 | SOCK_SEQPACKET | 有序分组套接字 | IPPROTO_SCTP | SCTP传输协议 |
AF_ROUTE | 路由套接字 | SOCK_RAM | 原始套接字 | ||
AF_KEY | 秘钥套接字 |
TCP客户用connect函数来建立与TCPserver的链接。
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); /*返回:若成功则为0。若出错则为-1*/
sockfd是由socket函数返回的套接字描写叙述符。第二个、第三个參数各自是一个指向套接字地址结构的指针和该结构的大小。客户在调用函数connect前不必非得调用bind函数,由于假设须要的话,内核会确定源IP地址,并选择一个暂时port号作为源port。假设是TCP套接字,调用connect函数将激发TCP的三路握手过程,并且仅在连接建立成功或出错时才返回,当中出错返回可能有下面几种情况:
a、若TCP客户没有收到SYN分节的响应,则返回ETIMEDOUT错误。
b、若对客户的SYN的响应是RST(表示复位),则表明该server主机在我们指定的port上没有进程在等待与之连接。
c、若客户发出的SYN在中间的某个路由器上引发了一个“destination unreachable”ICMP错误。则觉得是一个软错误。
bind函数把一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址与16位的TCP或UDPport号的组合。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);/*返回,成功则为0,出错则为-1*/
第二个參数是一个指向特定于协议的地址结构的指针。第三个參数是该地址结构的长度,对于TCP。调用bind函数能够指定一个port号。或指定一个IP地址,也能够两者都指定。还能够都不指定。
server在启动时捆绑它们的众所周知port。假设一个TCP客户或server未曾调用bind捆绑一个port,当调用connect或listen时。内核就要为对应的套接字选择一个暂时port号。让内核选择暂时port对于TCP客户来说是正常的。除非应用须要一个预留port;而毁于TCPserver来说却极为罕见,由于server是通过他们的众所周知port被大家认识的。
进程能够把一个特定的IP地址捆绑到它的套接字上,只是这个IP地址必须属于其所在主机的网络接口之中的一个。
假设指定port号为0,那么内核就bind被调用时选择一个暂时port。然而假设指定IP地址为通配地址。那么内核将等到套接字已连接TCP或已在套接字上发出数据报时才选择一个IP地址。对于IPv4来说,通配地址由常量INADDR_ANY来指定,其值为0。
注意:假设让内核来为套接字选择一个暂时port号,那么必须注意。函数bind并不返回所选择的值。实际上。因为bind函数的第二个參数有const限定词,它无法返回所选之值。
为了得到内核所选择的这个暂时port值,必须调用函数getsockanme来返回协议地址。
listen函数仅由TCPserver调用,它做两件事:
1、当socket函数创建一个套接字时,它被如果为一个主动套接字,也就是说。它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应该受指向该套接字的连接请求。
2、本函数的第二个參数规定了内核应该为对应套接字排队的最大连接个数。
#include <sys/socket.h>
int listen(int sockfd, int backlog);/*返回:若成功则为0。出错则为-1*/
本函数通常应该在调用socket和bind这两个函数之后。并在调用accept函数之前调用。
为理解backlog參数。必须认识到内核为不论什么一个给定的监听套接字维护两个队列:
1、未完毕连接队列。每一个这种SYN分节相应当中一项:已由某个客户发出并到达server,而server正在等待完毕相应的TCP三路握手过程,这些套接字处于SYN_RCVD状态
2、已完毕连接队列,每一个已完毕TCP三路握手过程的客户相应当中一项。这些套接字处于ESTBLISHED状态。
accept函数由TCPserver调用,用于从已完毕连接队列返回下一个已完毕连接。假设已完毕连接队列为空,那么进程被投入睡眠。
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); /*返回:若成功则为负描写叙述符。若出错则为-1*/
參数cliaddr和addrlen用来返回已连接的对端进程协议地址。假设accept成功,那么其返回值是由内核自己主动生成的一个全新描写叙述符,代表与所返回客户的TCP链接。在讨论accept函数时,称第一个參数为监听套接字描写叙述符。称返回值为已连接套接字描写叙述符。区分这两个套接字很重要。
一个server通常只创建一个监听套接字,它在该server的生命周期内一直存在。
内核为每一个由server进程接受的客户连接创建一个已连接套接字。当server完毕对某个给定客户的服务时,对应的一两节套接字就被关闭。
本函数最多返回3个值:一个既可能是新套接字描写叙述符也可能是出错仅仅是的整数、客户进程的协议地址以及该地址的大小。假设对返回客户协议地址不感兴趣,能够把cliaddr和addrlen均置为空指针。
close函数用来关闭套接字。并终止TCP连接。int close(int sockfd)。返回:成功则为0。出错则为-1。