shutdown 函数

2014-12-01 21:19:58   最后更新: 2014-12-02 00:11:04   访问数量:1288




终止网络连接的方法通常是调用 close 函数,不过 close 函数有以下两个限制:

  1. close 函数把描述符引用计数减 1,仅在引用计数变为 0 时,才会关闭套接字,而 shutdown 函数不管引用计数是多少都会直接发送 FIN 激发 TCP 正常连接终止序列
  2. TCP 连接是全双工的,有时我们只需要告知对端我们已经完成了数据发送,而对端仍有数据传送给我们

下图展示了使用 shutdown 函数在批量输入的情况

 

 

int shutdown(int sockfd, int howto);

 

定义于 sys/socket.h 中

若调用成功返回0,否则返回-1

 

参数说明 -- howto

该函数的行为依赖于 howto 参数的值

参数说明 -- howto
取值说明描述
SHUT_RD关闭连接的读这一般套接字中不再有数据可接收,而且套接字接收缓冲区中的现有数据都被丢弃,进程不能再对这样的套接字调用任何读函数
SHUT_WR关闭连接的写这一段对于 TCP 套接字,关闭了写一半被称为“半关闭”,当前留在套接字缓冲区中的数据将被发送掉,后跟 TCP 的正常连接终止序列,进程不能再对这样的套接字调用任何写操作函数
SHUT_RDWR关闭连接的读半部和写半部与分别用前两个参数调用一次shutdown函数的效果是一样的

下面的程序是基于 select 函数 改进而成

针对文中的两个问题均做了解决,但是read和write并没有进行必要的封装:

/* * author: liuzeyu * date: 2014-12-01 * file: function.c */ #include "function.h" int connect_serv(int sockfd, char *serv_ip) { struct sockaddr_in servaddr; if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) <= 0) { perror ("socket error"); exit(-1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, serv_ip, &servaddr.sin_addr.s_addr); if (connect (sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) { perror ("connect error"); exit(-1); } return sockfd; } void str_cli (FILE *fp, int sockfd) { int maxfdp1, stdineof; fd_set rset; char buf[MAXLINE]; int n; stdineof = 0; FD_ZERO (&rset); while (1) { if (stdineof == 0) FD_SET (fileno(fp), &rset); FD_SET (sockfd, &rset); maxfdp1 = max(fileno(fp), sockfd) + 1; if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0) { perror ("select error"); exit(-1); } if (FD_ISSET(sockfd, &rset)) { before_read_sock: n = read(sockfd, buf, MAXLINE); if (n < 0) { if (errno == EINTR) goto before_read_sock; else { perror("read error"); exit(-1); } } else if (n == 0) { if (stdineof == 1) return; else { printf("str_cli: sever terminated prematurly\n"); exit(-1); } } before_write: n = write(fileno(stdout), buf, n); if (n < 0) { if (errno == EINTR) goto before_write; else { perror("write error"); exit(-1); } } } if (FD_ISSET(fileno(fp), &rset)) { before_read_fp: n = read(fileno(fp), buf, MAXLINE); if (n < 0) { if (errno == EINTR) goto before_read_fp; else { perror("read error"); exit(-1); } } else if (n == 0) { stdineof = 1; if (shutdown(sockfd, SHUT_WR) < 0) { perror("shutdown"); exit(-1); } FD_CLR(fileno(fp), &rset); continue; } writen(sockfd, buf, n); } } } int max (int a, int b) { return a>b ? a : b; } ssize_t writen (int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ((nwritten = write (fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else { perror ("write error"); exit(-1); } } nleft -= nwritten; ptr += nwritten; } return n; } ssize_t readline (int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n=1; n<maxlen; n++) { again: if ((rc = read(fd, &c, 1)) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { *ptr = 0; return n-1; } else { if (errno == EINTR) goto again; perror("read:"); exit(-1); } } *ptr = 0; return n; }

 

 

这样,只有当标准输入已经读到EOF并且套接字上遇到EOF时,才将整个程序退出

同时,我们不再使用基于缓冲的 stdin 与 stdout,而是改用了 read 和 write 函数对缓冲区进行操作

 






读书笔记      技术帖      linux      unix      unp      unix网络编程      tcp      网络编程      socket      龙潭书斋      close      shutdown     


京ICP备15018585号