1.TCP 套接字编程API
1.TCP套接口编程--服务器函数
1.1.TCP函数调用流程图
套接字工作过程如下:
服务器首先启动,通过调用socket()建立一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用listen()使套接字做好侦听准备,并规定它的请求队列的长度,之后调用accept()来接收链接。客户在建立套接字之后就可调用connect()和服务器连接。连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据。最后待数据传输结束后,双方调用close()关闭套接字。
1.2.socket函数
int socket(int family,int type,int protocol);
描述:进行TCP/IP通信之前必须建一个套结字,它规定了我们使用什么协议,使用什
么数据包,有了这些规定以后我们才好传数据。它的返回值我们叫做套结字描述
符(socket descriptor),简写(sockfd)。
参数: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----------SCTP传输协议
返回:成功返回一个非负的整数,出错返回-1;
1.3.bind 函数
int bind(int sockfd,const struct sockadd *myaddree,socklen_t addrlen)
描述:调用bind可以指定IP地址或端口,可以两者都指定,也可以不指定,一般我们
指定的IP地址就是本机的IP地址,端口就是提供某种服务的端口。
其实就是将 套结字和IP地址和端口绑起来;这样的话在某一端我们就知道了使
用什么协议,自己这里的IP地址和端口。
参数:sockfd:套结字描述符;
Sockadd 里面含有本定地址和端口,也可以将地址设置为通配地址,
INADDR_ANY来指定通配地址,一般为0。它告知内核去选IP地址。
Addrlen:就是指前面那个结构体的长度。
struct sockaddr_in (“in” 代表 “Internet”)
struct sockaddr_in {
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* Internet地址 */
unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大 小)*/
};
返回:bind返回的常见错误是EADDRINUSSE;
1.4.Listen 函数
int listen(int sockfd,int backlog)
描述:listen()函数是等待别人连接,进行系统侦听请求的函数。当有人连接你的时候,
你有两步需要做:通过listen()函数等待连接请求,然后使用accept()函数来处理。
(accept()函数在下面介绍)。那么我们需要指定本地端口了,因为我们是等待别
人的连接。所以,在listen()函数调用之前,我们需要使用bind() 函数来指定使
用本地的哪一个端口数值。
参数: sockfd 是一个套接字描述符,由socket()系统调用获得。
backlog 是未经过处理的连接请求队列可以容纳的最大数目。
backlog 具体一些是什么意思呢?每一个连入请求都要进入一个连入请求队
列,等待listen 的程序调用accept()(accept()函数下面有介绍)函数来接受这
个连接。当系统还没有调用accept()函数的时候,如果有很多连接,那么本地
能够等待的最大数目就是backlog 的数值。你可以将其设成5 到10 之间的数
值(推荐)。
返回:listen()如果返回 –1 ,那么说明在listen()的执行过程中发生了错误。全局变量
errno 中存储了错误代码。
1.5.TCP的三次握手
当客户端SYN到达时,TCP再未完成连接的队列中创建一个新项,然后服务器SYN响应,其中捎带对客户SYN的ACK,这一项一直保存再未完成连接队列中,直到客户端对服务器
SYN的ACK到达或者超时为止。如果三次握手正常完成,该项就从未完成连接队列移到以完成队列的队尾。
当进程调用accept()的时候,以完成连接队列中的对头项将返回给进程。
1.6.accept 函数
int accept(int sockfd, void *addr, int *addrlen);
描述:accept()
l 有人从很远很远的地方尝试调用connect()来连接你的机器上的某个端口(当然是
你已经在listen()的)。
l 他的连接将被listen 加入等待队列等待accept()函数的调用(加入等待队列的最多
数目由调用listen()函数的第二个参数backlog 来决定)。
l 你调用accept()函数,告诉他你准备连接。
l accept()函数将回返回一个新的套接字描述符,这个描述符就代表了这个连接!
好,这时候你有了两个套接字描述符,返回给你的那个就是和远程计算机的连接,而
第一个套接字描述符仍然在你的机器上原来的那个端口上listen()。
1)服务器创建一个监听接口,它再服务器生命周期中一直存在。
2)内核为每个服务器进程接收的连接创建一个以连接套接口(也就是说三次握手已经完成)。
当服务器完成对于某个给定客户的服务时,相应的连接套接口就关闭。
这时候你所得到的那个新的套接字描述符就可以进行send()操作和recv()操作了。
参数:
sockfd :是正在listen() 的一个套接字描述符。
addr :一般是一个指向struct sockaddr_in 结构的指针;里面存储着远程连接过来的
计算机的信息(比如远程计算机的IP 地址和端口)。
Addrlen: 是一个本地的整型数值,在它的地址传给accept() 前它的值应该是
sizeof(struct sockaddr_in);accept()不会在addr 中存储多余addrlen bytes 大
小的数据。如果accept()函数在addr 中存储的数据量不足addrlen,则accept()
函数会改变addrlen 的值来反应这个情况。
返回:如果调用accept()失败的话,accept()函数会返回 –1 来表明调用失败,同时全局变量
errno 将会存储错误代码。
1.7.并发服务器
Unix编写并发服务器程序最简单的方法就是fock一个子进程来服务每个客户。
当一个连接建立时,accept返回,服务器接着调用fock,然后子进程服务客户(通过已连接套接口connfd),父进程则等待另一个连接。
我们可以想想,一个端口要给很多客户提供服务,所以它就建立那么多的子进程来提供服务,自己只管接收连接。
1.8.fork和exec函数
Fork调用一次,它却返回两次,它在调用进程(称为父进程)中返回一次,返回值是新派生进程(子进程)ID号,在子进程中又返回一次,返回值为0。
1.9. getpeername()函数
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
描述:
这个函数可以取得一个已经连接上的套接字的远程信息(比如IP 地址和端口),告诉
你在远程和你连接的究竟是谁.
参数:
sockfd 是你想取得远程信息的那个套接字描述符。
addr 是一个指向struct sockaddr (或是struct sockaddr_in)的指针。
addrlen 是一个指向int 的指针,应该赋于sizeof(struct sockaddr)的大小。
返回:
如果在函数执行过程中出现了错误,函数将返回 –1 ,并且错误代码储存在全局变量
Errno 中。
当你拥有了远程连接用户的IP 地址,你就可以使用inet_ntoa() 或gethostbyaddr()来输
出信息或是做进一步的处理。
1.10. gethostname()函数
int gethostname(char *hostname, size_t size);
描述:
gethostname()函数可以取得本地主机的信息.它比getpeername()要容易使用一些。
它返回正在执行它的计算机的名字。返回的这个名字可以被gethostbyname()函数使用,
由此可以得到本地主机的IP 地址。
参数:
hostname 是一个指向字符数组的指针,当函数返回的时候,它里面的数据就是本
地的主机的名字.
size 是hostname 指向的数组的长度.
返回:函数如果成功执行,它返回0,如果出现错误,则返回–1,全局变量errno 中存储着
错误代码。
2.TCP套接口编程--客户端函数
2.1.connect函数
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
描述:如果是TCP套接口,调用connect函数将激发TCP三次握手过程,而且紧连接成功
或出错返回。
Connect函数导致当前的套接字接口从CLOSED状态(建立套接字一直处于的状态)
转移到SYS_SEND状态,若成功则再转移倒ESTABLISHED状态。
注释:调用函数connect前不必非得调用bind函数,因为如果需要的话,内核会确定
源IP地址,并且选择临时端口。
参数:sockfd:是socket返回的套接字描述字。
Serv_addr:是一个存储远程计算机的IP地址和端口信息结构。
Addlen:应该是sizeof(struct sockaddr)
返回: