您的位置:首页 > 理论基础 > 计算机网络

4 基本TCP套接字编程

2013-02-24 16:59 295 查看

4 基本TCP套接字编程

4.1 socket 函数



此函数用于返回socket句柄,family、type、protocol其值如下图所示,其中protocol常常被置为0,系统会为我们选择默认值。







4.2 connet 函数



此函数用于TCP 客户端和服务器建立连接时用的,并且对于客户端(无论udp还是tcp,都不需要调用bind,内核会为我们做好这些事情)。函数中的第2、第3参数,均为服务端IP和port。调用connet函数之后,client和server开始建立3次握手。

connet返回出错的可能有以下3种:
1、TCP客户端发送SYN之后未收到响应,超时重传之后,会返回ETIMEOUT错误;
2、SYN之后收到服务端RST消息,返回ECONNREFUSED错误,server端返回RST有如下几种情况:

1、到不存在的端口连接请求,对于udp连接,如果目的端口没在使用,则会产生ICMP端口不可达消息,对于TCP则会产生rst消息。
2、异常终止一个连接,如果本端发生异常,无法正常关闭(发送fin消息),则只能发送rst消息。这样做有2个好处,本端丢弃未发送的数据;rst接收方可以区分是正常关闭还是异常关闭。
3、检测半打开连接,比如服务端已经关闭(异常关闭,如机器重启,或fin消息丢掉),而客户端并不知道服务端已经关闭,所以这个时候,客户端则处于半打开连接,如果这个时候客户端又向服务端发送数据,由于服务端已经断开连接(如重启机器),并不知道这个数据从哪来的,先前没有经过3次握手建立连接,则只能回复rst消息,告知客户端异常关闭。

3、收到ICMP目的端或网络不可达错误,超时重传,会返回EHOSTUNREACH或ENETUNREACH错误。
注意:按照TCP状态图,客户端从net之后,clost->syn_sent,收到ack之后,syn_sent->established。如果connet失败,未收到ack,则必须关闭socket,重新建立socket,再次connet,而不能反复调用connet。

4.3 bind函数



对于服务器一般都要调用bind函数来绑定本地IP和PORT,当然其IP可以为INADDR_ANY,通配,内核会为我们制定一个本机可用IP,如果port设定为0,也是通配,内核会为我们指定一个可用的。这个时候可以调用getsockname来获取内核指定的地址。当然了一般情况,服务器都会设定好ip地址和port,用不着内核指定。

4.4 listen函数



服务端需要调用的函数,调用完,状态变化为closed->listen。
服务器函数调用顺序:socket->bind->listen->accept
对于listen函数第2个参数,TCP服务器需要维护两个队列,未完成连接队列和已完成连接队列,在下面两个图中已经说明的很详细了。
当3此握手完成之后,则可以调用accept,并从已完成队列中的对头取一项accept,如果队列为空,服务器进程进入休眠状态。当然对于高并发的服务器,backlog值应该设定的大一些,目前看到的服务器,指为1000,经验值。
如果backlog值很小,两个队列已经满了,那么客户端新来的syn做和处理呢,服务器会忽略掉,这时客户端会超时重连的,继续发送syn。





4.5 accept函数



connected socket和listening socket:如果调用成功,cliaddr和addrlen则为客户端地址结构和长度,其返回值为connected socket句柄,第一个参数sockfd为listening socket,一个服务端通常创建一个listening socket,用于监听客户端connect请求,每次与客户端3次握手完成之后,就会创建一个connected socket,当与此客户端通信完成,则此socket需要关闭。


4.6 并发服务器

对于一个高并发的服务器,不可能在一个客户处花太长时间处理,而堵塞住不去处理其它客户请求,一个简单的做法如下:



每次accept之后,直接fork一个子进程,fork返回值如果是0,则表示当前进程为子进程,不为0则是子进程pid,则可以判断为主进程。在子进程中可以通过getppid函数获取主进程pid。
注意:如果fork一个子进程,其connected socket则需要在主、子进程中分别调用close来关闭掉。并且在子进程还需要单独关闭掉listening socket。这个地方就出现了一个问题,listenfd和connfd会被close多次,会不会有问题呢,因为对于connfd被close之后,则会给客户端发送finish消息,连接就断了,不用担心,对于socket句柄会有一个引用计数,当fork之后,connfd和listenfd都会被复制,引用计数加1,只有主次connfd都被close之后,才会真正close,发送finish消息。
下图就更形象的反应了这个过程。




4.7 getsockname和getpeername函数



一个获取本地,一个获取对端,以服务器为例,accept之后,如果用connfd作为参数,第一个获取的是本地ip和port,第二个获取对端客户端ip和port。
到此为止,TCP常用的socket函数以及介绍完毕,下面会给出一个简单的并发服务器(通过fork实现)和客户端demo。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: