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

基于TCP的半关闭

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

针对优雅断开的shutdown函数

#include
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
#include
#include
#include
#include
#include

#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 \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

下面介绍客户端代码:

#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  \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
int shutdown(SOCKET sock,int howto);//成功返回0.失败返回SOCKET_ERROR;
    sock   //要断开的套接字句柄
    howto  //断开方式的信息

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

1.SD_RECEIVE:断开输入流

2.SD_SEND:断开输出流

3.SD_BOTH:断开IO流

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

你可能感兴趣的:(TCP/IP网络编程,网络,tcp/ip,服务器,网络协议)