TCP/IP 网络编程 第七章:优雅地断开套接字连接

基于TCP的半关闭

在前面的章节中,我们都是通过close或者closesocket来断开套接字连接的,但是调用这两个函数导致我们套接字完全断开,套接字将无法接受数据,并且也只能传输完最后余留在缓冲区的数据内容。此时"只关闭一部分数据交换中使用的流"的方法应运而生。

针对优雅断开的shutdown函数

#include<sys/socket.h>
int shutdown(int sock,int howto);//成功时返回0,失败时返回-1
    sock    //需要半断开的文件描述符、
    howto   //进行半断开的方式

此函数的第二个参数可能是下面之一:

1.SHUT_RD//断开输入流

2.SHUT_WR//断开输出流

3.SHUT_RDWR//同时断开IO流

为何需要半关闭

试想一个场景,在客户端和服务端建立连接后,服务端向客户端传递文件,当服务端传递完文件后,客户端需要发送一个"Thank you"给服务端。这里就有一个问题,客户端该何时知道它应该发送“Thank you”给服务端。如果服务端通过close关闭套接字发EOF给客户端的话,服务端将再也无法接受“Thank you”。因此如果服务端只用关闭它的输出流,并且传递EOF给客户端的话就能够解决这个问题。shutdown函数的半关闭可以同时做到上述的两个需求。


基于半关闭的文件传输程序

下面介绍服务器的代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#defind BUF_SIZE 30;
void error_handling(char*message);

int main(int argc,char *argv[]){
    int serv_sd,clnt_sd;
    FILE *fp;
    char buf[BUF_SIZE];
    int read_cnt;

    struct sockaddr_in serv_addr,clnt_addr;
    socklen_t clnt_addr_sz;

    if(argc!=2){
        printf("Usage: %s <port>\n",argv[0]);
        exit(1);
    }

    fp=fopen("file_server.c","rb");
    serv_sd=socket(PF_INET,SOCK_STREAM,0);

    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_familiy=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(atoi(argv[1]));

    bind(serv_sd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    listen(serv_sd,5);

    clnt_addr_sz=sizeof(clnt_addr);
    clnt_sd=accept(serv_ad,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);

    while(1){
       read_cnt=fread((void*)buf,1,BUF_SIZE,fp);
       if(read_cnt<BUF_SIZE){
           write(clnt_sd,buf,read_cnt);
           break;
       }
       write(clnt_sd,buf,BUF_SIZE);
    }

    shutdown(clnt_sd,SHUT_WR);
    read(clnt_sd,buf,BUF_SIZE);
    printf("Message from client: %s \n",buf);

    fclose(fp);
    close(clnt_sd);close(serv_sd);
    return 0;
}

void error_handling(char *message){
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

下面介绍客户端代码:

#include<"与服务器头文件声明一致,故省略">
#defind BUF_SIZE 30
void error_handling(char *message);

int main(int argc,char*argv[]){
    int sd;
    FILE *fp;

    char buf[BUF_SIZE];
    int read_cnt;
    struct sockaddr_in serv_addr;
    if(argc!=3){
        printf("Usage: %s <IP> <port>\n",argv[0]);
        exit(1);
    }

    fp=fopen("receive.dat","wb");
    sd=socket(PF_INET,SOCK_STREAM,0);

    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));

    connect(sd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    while((read_cnt=read(sd,buf,BUF_SIZE))!=0)
         fwrite((void*)buf,1,read_cnt,fp);

    puts("Reveived file data");
    write(sd,"Thank you",10);
    fclose(fp);
    close(sd);
    return 0;
}

void error_handling(char*message){
    //与服务器的内容一致   
}

基于Windows的实现

Windows平台同样通过调用shutdown函数完成半关闭,只是想起传递的参数名略有不同。

#include<winsock2.h>
int shutdown(SOCKET sock,int howto);//成功返回0.失败返回SOCKET_ERROR;
    sock   //要断开的套接字句柄
    howto  //断开方式的信息

上述函数的第二个参数的可能值及其含义如下:

1.SD_RECEIVE:断开输入流

2.SD_SEND:断开输出流

3.SD_BOTH:断开IO流

虽然这些常量名不同于Linux中的名称,但是其内部的值完全相同。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Reol520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值