基本套接字

基本套接字函数

创建套接字

#include <sys/socket.h>
int socket (int family, int type, int protocol);

a a
a a

返回套接字描述符

客户端调用

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
  1. 客户端没收到SYN包(超时),返回ETIMEDOUT
  2. 服务端回复RST,表明对应端口没有进程监听,产生硬件错误(hard error)返回ECONNREFUSED
    • RST是在出错的情况下TCP发送的包,共三种情况,其余两种是:1.TCP终止已存在的连接,2.TCP收到不存在的连接的包(即连接已经关闭后收到包)
  3. SYN从中间路由器收到ICMP"destination unreachable",称为软错误(soft error)。客户端保存信息,持续发送SYN直到超时,返回EHOSTUNREACH或ENETUNREACH;也有可能本地所有转发表都无法到达,connect立即返回。如果connect失败,套接字不可用,不能再次调用connect,必须调用close,重新调用socket。

bind调用将本地协议地址赋值给套接字

#include <sys/socket.h>
int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

bind的主要错误是EADDRINUSE,涉及到SO_REUSEADDR和SO_REUSEPORT套接字选项。

#include <sys/socket.h>
#int listen (int sockfd, int backlog);

listen调用执行两步操作:

  1. 当套接字通过socket()创建就视为活动的套接字。如果是客户端,会调用connect;lixten()调用将套接字转换为被动套接字,表示内核应该接受连接请求。
  2. 第二个参数指明内核为这个套接字分配队列的最大连接数量(最好的方式是包装listen函数,支持配置、环境变量指定数量,避免重新编译)。内核为lintening套接字维护两个队列,未完成连接队列:包含所有收到客户端的SYN项,等待完成TCP的三步握手,处于SYN_RCVD状态。已完成连接队列:包含已完成TPC三步握手项,处于ESTABLISHED状态。
    a
    当SYN到达server端时,TCP在未完成连接队列中创建一项,并完成三步握手中的第二步,直到第三步完成或超时,这一项都处于未完成连接队列。如果三步握手正常完成,这一项被移动到已完成队列的末尾。当进程调用accept(),已完成队列的第一项被返回,如果为空,进程会睡眠(默认阻塞套接字),直到有项被转移到已完成队列中,backlog的值是两个队列之和的最大值。在三步握手完成后,调用accept之前,客户端发送的数据存储在套接字接收缓冲区中,直到填满。
    如果客户端SYN到达时队列已满,TCP无视这个SYN,因为这个状况是临时的,可以视为软错误,客户端仅仅重发SYN即可。如果不想任何客户端连接listening套接字,最好关闭它而不是将backlog设为0(可能有多种实现,产生不同的解释)。

accept返回下一个已完成连接。如果进程会睡眠(默认阻塞套接字)。

#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

accept成功则返回内核自动产生的新描述符,代表TCP连接,称为连接套接字。服务结束后,连接描述符关闭。函数返回三个值,描述符。客户端地址,地址长度。如果不需要后两个值,可将指针设为NULL。


#include <unistd.h>
int close (int sockfd);

每调用一次close,减少描述符的引用计数,只有当引用计数为0时才发FIN包。

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen); /*get local protocol adddress*/
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen); /*get foreign procotol address*/
  • 客户端调用connect后,getsockname获取本地IP地址和端口
  • 服务端调用bind并设置端口号为0时,内核自动选取端口,getsockname获取本地地址与端口
  • getsockname还可以获取套接字的地址族
  • 服务端bind()通配的IP地址,accept返回后,连接套接字调用getsockname,获取为连接赋值的本地IP地址
  • 子进程调用exec函数族执行新进程,accept返回的cliaddr信息丢失,只能通过描述符调用getpeername得到客户端的IP地址和端口。新进程通常有两种方式获取连接描述符,一种是将描述符格式化为参数通过exec传递;一种是提前约定特定的描述符值。

信号处理

信号的作用是向进程通知一件事件的发生,可以由一个进程发送给另一个进程(或发给自己),也可以由内核发送给进程。当调用fork()产生子进程处理连接时,主要处理的信号时SIGCHILD,当一个进程终止时,内核向它的父进程发送这个信号,处理信号采取的默认行为是忽视,因此子进程进入僵尸状态。POSIX中指明,将SIGCHLID设为SIG_IGN的处理方式是未定义的,不能保证子进程不变成僵尸,可移植的方式是捕捉信号并调用wait()或waitpid()。 对于一个典型的echo server和echo client会产生如下输出

solaris % tcpserv & //start server in background
  
solaris % tcpcli01 127.0.0.1
hi there  //we type this
hi there  //this is echoed
^D        //we type our EOF character

child 16942 terminated //tcpserv  output by printf in signal handler
accept error: Interrupted system call //tcpserv main function aborts 
  1. 客户端输入EOF,客户端进程退出,并向server发送FIN,server返回ACK
  2. 服务端对应的子进程终止
  3. 向父进程发送SIGCHILD信号,调用signal handler处理,此时父进程正阻塞在accept
  4. 由于在阻塞期间捕捉到信号,accept(永远阻塞的慢系统调用)返回错误EINTR,代表系统调用被中断,而父进程没有处理这个错误,因此异常终止 有些系统signal()设置了SA_RESTART标志,内核会重新开始被中断的系统调用,因此不会出现上面的错误,但是为了可移植性,还是要手动包装signal()。可以使用下面这种方式:
for ( ; ; ) {
    clilen = sizeof (cliaddr);
    if ( (connfd = accept (listenfd, (SA *) &cliaddr, &clilen)) < 0) {
        if (errno == EINTR)
            continue;         /* back to for () */
        else
            err_sys ("accept error");
    }
    ...
    ...
}

几乎所有的慢系统调用都可以使用这种方式处理被中断情况(EINTR),但是connect()不可以,它需要使用类似select的I/O多路复用方法确定。I/O多路复用主要解决进程有多个I/O源的情况,进程不应该阻塞在一个源上,而是当任何一个源有事件发生时都可以得到通知。

wait和waitpid

如果在信号处理函数执行前产生了多个信号,处理函数只执行一次,因为Unix没有信号队列。
如果该进程有正在运行的子进程,在第一个子进程结束前,wait()会一直阻塞。waitpid可以设置WNOHANG标志,即使有正在运行的子进程也不必阻塞,可以使用下面的方法处理产生多次信号的情况:

 2 void
 3 sig_chld(int signo)
 4 {
 5     pid_t    pid;
 6     int      stat;

 7     while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
 8         printf("child %d terminated
", pid);
 9     return;
10 }

调用write()向已经收到FIN的套接字写数据,第一次对方会发送RST,第二次进程会收到SIGPIPE信号,write()返回EPIPE错误。

原文地址:https://www.cnblogs.com/zyfgs2012/p/4103396.html