免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 13826 | 回复: 9

请教关于socket的close和shutdown问题 [复制链接]

论坛徽章:
0
发表于 2006-05-19 23:38 |显示全部楼层
我查了很多关于socket的close和shutdown的材料,觉得还是晕晕的,请帮忙理解一下。
=====
引用1:
Close()和shutdown()——结束数据传输
  当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd);
  你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。
  int shutdown(int sockfd,int how);
  Sockfd的含义是显而易见的,而参数 how可以设为下列值:
  ·0-------不允许继续接收数据
  ·1-------不允许继续发送数据
  ·2-------不允许继续发送和接收数据,均为允许则调用close ()
  shutdown在操作成功时返回0,在出现错误时返回-1(并置相应errno

=====
问题:
(1)shutdown的how为2时的“均为允许则调用close ()”是什么意思?
(2)shutdown(socktfd,2)和close的功能一样吗?

=======
引用2
使用close中止一个连接,但它只是减少描述符的参考数,并不直接关闭连接,只有当描述符的参考数为0时才关闭连接。
shutdown可直接关闭描述符,不考虑描述符的参考数,可选择中止一个方向的连接。
=======
问题:
(3)close到底是关闭连接还是关闭套接字描述符?
(4)一般来说,都使用close,那么shutdown不是比close更好用吗?为什么不用?




请高手指点迷津,万分感谢!

论坛徽章:
0
发表于 2006-05-21 15:14 |显示全部楼层
关于close:
你的引用2是断章取义的。
调用close后,将中止通信、删除套接字、丢弃数据。但是,注意喽,但是,如果有多个进程共享一个套接字,这时出现引用2的情况,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。

论坛徽章:
0
发表于 2006-05-21 15:49 |显示全部楼层
关于shutdown:
“均为允许则调用close”这句实属多余,因为此时shutdown等同于close。
到这里,1、2、3问应该回答完了,不知是否认同。
至于说为什么close用得多一些,我想,shutdown适用于全双工的套接字,UDP中是用不到的,在TCP中又有多少人需要单向关闭连接呢。

论坛徽章:
0
发表于 2006-05-21 18:47 |显示全部楼层
嗯,有点明白了,是不是这样:

close是关闭描述符的,描述符涉及到参考数的问题,所以在多进程共同调用描述符时,close完成的只是减少参考数,一旦参考数为0,那么close就释放描述符所占用的系统资源。

shutdown是关闭两个描述符之间的连接的,但是它不涉及参考数的问题。
当how为2时同close,只是不计参考数;当how为0或1时也是不计参考数的中止对于描述符的读或写的连接。

如果是的话那么:
shutdown释放描述符所占的系统资源吗?(我记得哪里好像说close释放,shutdown不释放)

论坛徽章:
0
发表于 2006-05-21 19:30 |显示全部楼层
关于计数的问题,是有一个专门的计数量。
shutdown如果完全关闭的话,也是要释放的,单向关闭则不释放。
至于多进程的问题,shutdown如何工作不太清楚,但我认为在linux下是和close一样的。

论坛徽章:
0
发表于 2006-05-22 18:00 |显示全部楼层
谢谢,有如乌云中露出了阳光

论坛徽章:
0
发表于 2006-06-02 00:39 |显示全部楼层
shutdown是不会关闭文件操作符的,只有close才会关闭,可以写个测试程序试试看

论坛徽章:
0
发表于 2006-06-02 09:39 |显示全部楼层
按照unp的说法是
SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该套接字发出任何读操作。对TCP套接字该调用之后接受到的任何数据将被确认然后无声的丢弃掉。
SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作
SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR

close的操作则与SO_LINGER参数有关

论坛徽章:
0
发表于 2006-06-02 11:31 |显示全部楼层
我使用如下程序测试

服务器端:

// File: prg4_1.c
        #include <stdio.h>          /* These are the usual header files */  
        #include <strings.h>         /* for bzero() */
        #include <unistd.h>          /* for close() */
        #include <sys/types.h>  
        #include <sys/socket.h>  
        #include <netinet/in.h>  
        #include <arpa/inet.h>
        #include <stdlib.h>

        #define PORT 1234   /* Port that will be opened */  
        #define BACKLOG 1   /* Number of allowed connections */  

        main()  
        {  
        int listenfd, connectfd; /* socket descriptors */  
        struct sockaddr_in server; /* server's address information */  
        struct sockaddr_in client; /* client's address information */  
        int sin_size;  

        /* Create TCP socket */
        if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
        /* handle exception */
        perror("Creating socket failed.");
        exit(1);
        }

        /* set socket can be reused */
        int opt = SO_REUSEADDR;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        bzero(&server,sizeof(server));   /* fill server with 0s */
        server.sin_family=AF_INET;  
        server.sin_port=htons(PORT);  
        server.sin_addr.s_addr = htonl (INADDR_ANY);  
        if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {  
        /* handle exception */
        perror("Bind error.");
        exit(1);  
        }     

        if(listen(listenfd,BACKLOG) == -1){  /* calls listen() */  
        perror("listen() error\n");  
        exit(1);  
        }  

        sin_size=sizeof(struct sockaddr_in);  
        if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {
        /* calls accept() */  
        perror("accept() error\n");  
        exit(1);  
        }  

        printf("You got a connection from %s\n",inet_ntoa(client.sin_addr) ); /* prints client's IP */
        send(connectfd,"Welcome to my server.\n",22,0); /* send to the client welcome message */  

        if(shutdown(connectfd,0)==-1)
        {
                perror("shutdown cfd 0");
        }
       
        if(shutdown(connectfd,0)==-1)
        {
                perror("shutdown cfd 0");
        }
       
        if(shutdown(connectfd,1)==-1)
        {
                perror("shutdown cfd 1");
        }
       
        if(shutdown(connectfd,1)==-1)
        {
                perror("shutdown cfd 1");
        }
       
        if(shutdown(connectfd,2)==-1)
        {
                perror("shutdown cfd 2");
        }
       
        if(shutdown(connectfd,2)==-1)
        {
                perror("shutdown cfd 2");
        }

        if(shutdown(listenfd,0)==-1)
        {
                perror("shutdown lfd 0");
        }
       
        if(shutdown(listenfd,0)==-1)
        {
                perror("shutdown lfd 0");
        }
       
        if(shutdown(listenfd,1)==-1)
        {
                perror("shutdown lfd 1");
        }
       
        if(shutdown(listenfd,1)==-1)
        {
                perror("shutdown lfd 1");
        }
       
        if (shutdown(listenfd,2) == -1)            
        {
                perror("shutdown lfd 2");
               
        }
        if (shutdown(listenfd,2) == -1)         
        {
                perror("shutdown lfd 2");
               
        }
        if (close(connectfd) == -1)      
        {
                perror("close cfd");
               
        }
        if (close(connectfd) == -1)        
        {
                perror("close cfd");
               
        }
        if (close(listenfd) == -1)         
        {
                perror("close lfd");
               
        }
        if (close(listenfd) == -1)     
        {
                perror("close lfd");
                exit(1);
        }
       
}

客户端
// File: prg4_2.c
        #include <stdio.h>  
        #include <unistd.h>
        #include <strings.h>
        #include <sys/types.h>  
        #include <sys/socket.h>  
        #include <netinet/in.h>  
        #include <netdb.h>        /* netbd.h is needed for struct hostent  */
        #include <stdlib.h>

        #define PORT 1234   /* Open Port on Remote Host */  
        #define MAXDATASIZE 100   /* Max number of bytes of data */  

        int main(int argc, char *argv[])  
        {  
        int fd, numbytes;   /* files descriptors */  
        char buf[MAXDATASIZE];  /* buf will store received text */  
        struct hostent *he;         /* structure that will get information about remote host */  
        struct sockaddr_in server;  /* server's address information */  

        if (argc !=2) {       /* this is used because our program will need one argument (IP) */  
        printf("Usage: %s <IP Address>\n",argv[0]);  
        exit(1);  
        }  

        if ((he=gethostbyname(argv[1]))==NULL){ /* calls gethostbyname() */  
        printf("gethostbyname() error\n");  
        exit(1);  
        }  

        if ((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){  /* calls socket() */  
        printf("socket() error\n");  
        exit(1);  
        }  

        bzero(&server,sizeof(server));
        server.sin_family = AF_INET;  
        server.sin_port = htons(PORT); /* htons() is needed again */  
        server.sin_addr = *((struct in_addr *)he->h_addr);  /*he->h_addr passes "*he"'s info to "h_addr" */  

        if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr))==-1){ /* calls connect() */  
        printf("connect() error\n");  
        exit(1);  
        }  

        if ((numbytes=recv(fd,buf,MAXDATASIZE,0)) == -1){  /* calls recv() */  
        printf("recv() error\n");  
        exit(1);  
        }  

        buf[numbytes]='\0';  
        printf("Server Message: %s\n",buf); /* it prints server's welcome message  */  

        close(fd);   /* close fd */  
}

运行结果:
[root@localhost home]# ./test3
shutdown cfd 1: Transport endpoint is not connected
shutdown cfd 2: Transport endpoint is not connected
shutdown cfd 2: Transport endpoint is not connected
shutdown lfd 0: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
close cfd: Bad file descriptor
close lfd: Bad file descriptor

论坛徽章:
0
发表于 2006-06-04 18:12 |显示全部楼层
不好意思,上次贴完代码有事就忙别的了。

测试代码完成的功能是:服务器向客户端发送一个字符串,使用tcp连接。

在服务器发送完字符串后,我对连接套接字和监听套接字分别进行shutdown和close关闭:
(1)对于连接套接字和监听套接字分别调用了how为0.1.2三种方式,每种方式调用两次。所以共有12个shutdown函数调用。
(2)对于连接套接字和监听套接字调用了close,调用两次。所以共有4个close函数调用。

结果如下:
shutdown cfd 1: Transport endpoint is not connected
shutdown cfd 2: Transport endpoint is not connected
shutdown cfd 2: Transport endpoint is not connected
shutdown lfd 0: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
close cfd: Bad file descriptor
close lfd: Bad file descriptor

从测试代码可以看出:
(1)对于关闭写通道,只能关闭一次。
(2)由于已经关闭了读写通道,所以对于how=2时的两次调用都失败。
(3)关闭监听套接字和连接套接字没有关系,因为这是两个套接字。
(4)最后两行:shutdown没有关闭套接字描述符,close关闭了套接字描述符。


同时又衍生出了新的问题:使用shutdown方式关闭监听套接字为什么是这个结果?
shutdown lfd 0: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 1: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
shutdown lfd 2: Transport endpoint is not connected
关闭连接套接字时how=0没有任何反映,这里有一个关闭读错误。
调用how=1和=2都是错误。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP