socket单向断开函数shutdown

目录

1、函数声明

2、shutdown()与close()/closesocket()的区别

3、适用情景

4、代码


因为socket是双向,client和server都可以进行input和output,所以有时候我们需要数据在socket上实现单向的传输,即数据往一个方向传输。单向的socket为半开放Socket。

要实现半开放式,需要用到shutdown()函数。

1、函数声明

/* Shut down all or part of the connection open on socket FD.
   HOW determines what to shut down:
     SHUT_RD   = No more receptions;
     SHUT_WR   = No more transmissions;
     SHUT_RDWR = No more receptions or transmissions.
   Returns 0 on success, -1 for errors.  */
extern int shutdown (int __fd, int __how) __THROW;

SHUT_RD:断开输入流。套接字无法接收数据(即使缓冲区收到数据也会被清除),无法调用输入相关函数。

SHUT_WD:断开输出流。套接字无法发送数据,但如果输出缓冲区中还有未传输的数据,则将传递到目标主机。

SHUT_RDWR:同时断开I/O流。相当于分两次调用shutdown(),其中一次以SHUT_RD为参数,另一次以SHUT_WR为参数。

2、shutdown()与close()/closesocket()的区别

在多进程环境中:

close()/closesocket()破坏当前的进程所引用的socket标识符,但是socket还是完好存在的,别的进程还是可以照样使用的(就像shared_ptr一样),当引用数降为0的时候,socket被释放销毁。

shutdown() 用来关闭连接,而不是套接字,不管调用多少次 shutdown(),套接字依然存在,直到调用 close() / closesocket() 将套接字从内存清除。而shutdown()的杀伤面积就比较大了,假设有一对父子进程公用一个socket,如果父进程对此socket对应的标识符进行了shutdown(fd,SHUT_RD)操作,则此socket的所有引用操作符均无法接收数据,只能发送数据了。

调用 close()/closesocket() 关闭套接字时,或调用 shutdown() 关闭输出流时,都会向对方发送 FIN 包。FIN 包表示数据传输完毕,计算机收到 FIN 包就知道不会再有数据传送过来了。

默认情况下,close()/closesocket() 会立即向网络中发送FIN包,不管输出缓冲区中是否还有数据,而shutdown() 会等输出缓冲区中的数据传输完毕再发送FIN包。也就意味着,调用 close()/closesocket() 将丢失输出缓冲区中的数据,而调用 shutdown() 不会。

3、适用情景

因为shutdown可以让socket实现单向传输,而半开放socket使用于以下场合:

  • 当你想要确保所有写好的数据已经发送成功时。如果在发送数据的过程中,网络出现断开或者异常,系统不一定会返回异常,这时候你要怎么确定对端已经接收到数据了呢? 可以使用shutdown()函数来确定数据是否发送成功,因为调用shutdown()函数时只有在缓存中的数据全部发送成功后才会返回。
  • 当不能往有些socket上写数据,或者不能从有些socket上读数据时,可以使用shutdown让其断开,后续对其的写或者读的操作将会返回异常。
  • 当多线程时,想防止其他线程或进程访问到该资源是,有或者你想彻底关闭这个socket,让别的线程或者进程也没法使用则可以调用shutdown()。

4、代码

/*================================================================
 *   Copyright (C) 2021 baichao All rights reserved.
 *
 *   文件名称:shutdownService.cpp
 *   创 建 者:baichao
 *   创建日期:2021年02月08日
 *   描    述:
 *
 ================================================================*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(){
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(11230);  //端口
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    //进入监听状态,等待用户发起请求
    listen(serv_sock, SOMAXCONN);


    //接收客户端请求
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);

    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    int count = 0;
    int size = 3;
    while(size--)
    {

        //sleep(5);
        char str[100];
        int readNum = read(clnt_sock,str,sizeof(str));
        if(readNum <= 0)
            continue;
        std::cout<<"第"<<++count<<"次读取数据长度为:"<<readNum<<std::endl;
        // write(clnt_sock, , sizeof(str));

    }
    shutdown(clnt_sock,SHUT_RD);

    size = 3;
    while(size--)
    {

        //sleep(5);
        char str[100];
        int readNum = read(clnt_sock,str,sizeof(str));
        if(readNum <= 0)
            continue;
        std::cout<<"第"<<++count<<"次读取数据长度为:"<<readNum<<std::endl;
        // write(clnt_sock, , sizeof(str));

    }
    close(clnt_sock);
    close(serv_sock);
    return 0;
}
/*================================================================
 *   Copyright (C) 2021 baichao All rights reserved.
 *
 *   文件名称:shutdownClient.cpp
 *   创 建 者:baichao
 *   创建日期:2021年02月08日
 *   描    述:
 *
 ================================================================*/

#include<iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(){

    int serv_sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(11230);

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

    char context[100] = {0};
    for(int i = 0; i < sizeof(context)/sizeof(context[0]); ++i)
        context[i] = '1';
    int size = 3;
    while(size--)
    {
        write(serv_sock,context,sizeof(context)-1);
    }
    sleep(5);
    size = 3;
    while(size--)
    {
        write(serv_sock,context,sizeof(context)-1);
    }
    close(serv_sock);

    return 0;
}

运行结果:

猜你喜欢

转载自blog.csdn.net/weixin_40179091/article/details/113669146