TCP/IP Network Programming Chapter 7: Disconnecting Socket Connections Gracefully

TCP-based half-close

In the previous chapters, we used close or closesocket to disconnect the socket connection, but calling these two functions will cause our socket to be completely disconnected, the socket will not be able to accept data, and the data content remaining in the buffer can only be transmitted. At this time, the method of "closing only part of the streams used in data exchange" came into being.

shutdown function for graceful disconnection

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

The second argument to this function may be one of the following:

1.SHUT_RD//Disconnect the input stream

2.SHUT_WR//Disconnect the output stream

3.SHUT_RDWR//Disconnect the IO stream at the same time

Why half-close

Imagine a scenario. After the client and server establish a connection, the server transfers the file to the client. When the server finishes transferring the file, the client needs to send a "Thank you" to the server. There is a problem here, when should the client know that it should send "Thank you" to the server. If the server closes the socket and sends EOF to the client, the server will no longer be able to accept "Thank you". So if the server can just close its output stream and pass EOF to the client, it can solve this problem. The half-close of the shutdown function can meet the above two requirements at the same time.


Half-close based file transfer program

Here is the server code:

#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);
}

Here is the client code:

#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-based implementation

The Windows platform also completes the half-shutdown by calling the shutdown function, but the parameter name passed is slightly different.

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

The possible values ​​of the second parameter of the above function and their meanings are as follows:

1.SD_RECEIVE: disconnect the input stream

2.SD_SEND: disconnect the output stream

3.SD_BOTH: Disconnect the IO stream

Although the names of these constants are different from the names in Linux, the internal values ​​are exactly the same.

Guess you like

Origin blog.csdn.net/Reol99999/article/details/131717971