目次
2. shutdown()とclose()/ closesocket()の違い
ソケットは双方向であるため、クライアントとサーバーの両方が入出力を実行できます。そのため、ソケットでデータを一方向に送信する、つまりデータを一方向に送信する必要がある場合があります。一方向ソケットはセミオープンソケットです。
セミオープンを実現するには、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()を2回呼び出すのと同じで、1つはSHUT_RDをパラメーターとして受け取り、もう1つはSHUT_WRをパラメーターとして受け取ります。
2. shutdown()とclose()/ closesocket()の違い
マルチプロセス環境の場合:
close()/ closesocket()は、現在のプロセスによって参照されているソケットIDを破棄しますが、ソケットはそのまま存在し、他のプロセスは引き続きそれを使用できます(shared_ptrと同様)。参照数が0に減少すると、ソケットは解放されました。そして破壊されました。
shutdown()は、ソケットではなく接続を閉じるために使用されます。shutdown()が何度呼び出されても、close()/ closesocket()が呼び出されてソケットがメモリからクリアされるまで、ソケットは存在し続けます。shutdown()のkill領域は比較的大きいです。親プロセスと子プロセスのペアがソケットを共有していると仮定すると、親プロセスがこのソケットに対応する識別子に対してシャットダウン(fd、SHUT_RD)操作を実行すると、すべての参照演算子このソケットのはデータを受信できず、データを送信することしかできません。
close()/ closesocket()を呼び出してソケットを閉じるか、shutdown()を呼び出して出力ストリームを閉じると、FINパケットが相手側に送信されます。FINパケットは、データ送信が完了したことを示し、コンピューターは、FINパケットの受信後にこれ以上データが送信されないことを認識しています。
デフォルトでは、close()/ closesocket()は、出力バッファーにデータがあるかどうかに関係なく、すぐにFINパケットをネットワークに送信し、shutdown()は、出力バッファー内のデータが送信されるのを待ってから、 FINパケット。つまり、close()/ closesocket()を呼び出すと、出力バッファー内のデータが失われますが、shutdown()を呼び出すと失われません。
3.該当するシナリオ
シャットダウンにより、ソケットは一方向の送信を実現できるため、セミオープンソケットは次の状況で使用されます。
- 書き込まれたすべてのデータが正常に送信されたことを確認する場合。データ送信の過程でネットワークが切断されたり異常になったりしても、必ずしもシステムが異常を返すとは限りませんが、このとき、ピアがデータを受信したことをどのように判断しますか?shutdown()関数を使用すると、データが正常に送信されたかどうかを判断できます。shutdown()関数が呼び出されると、バッファー内のすべてのデータが正常に送信された後にのみ返されるためです。
- 一部のソケットにデータを書き込めない場合、または一部のソケットからデータを読み取ることができない場合は、shutdownを使用してソケットを切断すると、後続の書き込みまたは読み取り操作で例外が返されます。
- マルチスレッドの場合、他のスレッドまたはプロセスがリソースにアクセスできないようにする場合、ソケットがある場合、またはソケットを完全に閉じて他のスレッドまたはプロセスが使用できないようにする場合は、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;
}
演算結果: