ネットワークプログラミングソケット、OSI 7層アーキテクチャの各層プロトコルの最も包括的な説明

ソケット原理

移動:https : //www.jianshu.com/p/066d99da7cbd

1.ソケットとは

コンピュータ通信の分野では、ソケットは、コンピュータ間で「ソケット」として翻訳される通信規則又は一つの方法。ソケットアグリーメントを通じて、コンピュータは他のコンピュータからデータを受信したり、他のコンピュータにデータを送信したりできます。
  ソケットはUnixに由来し、Unix / Linuxの基本的な哲学の1つは「すべてがファイルである」ということです。「open open- >読み取りおよび書き込み書き込み/読み取り->「閉じる」モードを閉じて操作します。
  私の理解では、Socketはこのパターンの実装です。そのソケットは特別なファイルであり、一部のソケット関数はその操作(読み取り/書き込みIO、オープン、クローズ)です。
  Socket()関数は整数のソケット記述子を返し、接続の確立やデータ送信などの後続の操作はソケットを介して実装されます。

2.プロセスがネットワーク上でどのように通信するか

ソケットは主にネットワーク通信を解決するために使用されるため、プロセスがネットワーク上でどのように通信するかを理解しましょう。

2.1、ローカルプロセス間通信

a。メッセージの受け渡し(パイプライン、メッセージキュー、FIFO)
  b。同期(ミューテックス、条件変数、読み書きロック、ファイルおよび書き込みレコードロック、セマフォ)?[あまり明確ではない]
  c。共有メモリ(匿名および名前付き、例:チャネル)
  d。リモートプロシージャコール(RPC)

2.2。プロセスがネットワークで通信する方法

プロセスがネットワーク上でどのように通信するかを理解するには、2つの問題を解決する必要があります:
  a。ホストを識別する方法、つまり、通信するプロセスが実行されているホストを特定する方法。
  b。一意のプロセス、pidによるローカル識別をどのように識別しますか、ネットワークでどのように識別する必要がありますか?
解決策:
  a。TCP / IPプロトコルファミリーがこの問題を解決しました。ネットワークレイヤーの「IPアドレス」は、ネットワーク内のホストを一意に識別できます
  。b。トランスポートレイヤーの「プロトコル+ポート」は、ホスト内のアプリケーションを一意に識別できます。 (プロセス)したがって、トリプル(IPアドレス、プロトコル、ポート)を使用してネットワークのプロセスを識別でき、ネットワーク内のプロセス通信はこのフラグを使用して他のプロセスと対話できます。

3.ソケットと通信する方法

これで、ネットワーク内のプロセス間で通信する方法がわかりました。つまり、トリプル[IPアドレス、プロトコル、ポート]を使用してネットワーク間で通信できます。次に、どのようにしてそれを実現すればよいのか、したがって、ソケットが生まれました。トリプレットはネットワーク通信用のミドルウェアツールであり、現在ほとんどのアプリケーションがUNIX BSDのソケット(ソケット)やUNIX System VのTLI(廃止された)などのソケットを使用しています。
ソケット通信には、一般的に使用される2つのデータ転送方法があります:
  a。SOCK_STREAM:コネクション型のデータ転送方法を示します。データはエラーなしで別のコンピューターに到達できます。データが損傷または失われた場合、再送信できますが、効率は比較的遅くなります。一般的なhttpプロトコルは、データの正確性を保証する必要があるため、SOCK_STREAMを使用してデータを送信します。そうしないと、Webページを正常に解析できません。
  b。SOCK_DGRAM:コネクションレスのデータ送信方法を示します。コンピュータはデータを送信するだけで、データの検証は行いません。データが送信中に破損したり、別のコンピュータに到達しない場合、データを修復する方法はありません。つまり、データは間違っており、再送信できません。SOCK_DGRAMは検証作業が少ないため、効率はSOCK_STREAMよりも高くなります。
  例:QQビデオチャットとボイスチャットは、SOCK_DGRAMを使用してデータを送信します。何よりもまず、通信の効率を確保し、遅延を最小限に抑える必要があるため、データの一部が失われたとしても、ビデオとオーディオは、ノイズまたはノイズまでの通常の分析は、通信品質に大きな影響を与えません

4. TCP / IPプロトコル

4.1コンセプト

TCP / IP [TCP(伝送制御プロトコル)およびIP(インターネットプロトコル)]は、宛先でデータをカプセル化、アドレス指定、送信、ルーティング、および受信する方法を標準化するポイントツーポイントリンクメカニズムを提供します。ソフトウェア通信プロセスを4つの抽象レイヤーに抽象化し、プロトコルスタックを採用してさまざまな通信プロトコルを実装します。プロトコルファミリのさまざまなプロトコルは、機能に応じてこれらの4つの階層構造に分類され、単純化された7層OSIモデルと見なされることがよくあります。

それらの間は、配送ラインとステーションの役割に似ています。たとえば、配送ステーションを提案するには、配送の詳細を理解する必要があります。

TCP(Transmission Control Protocol、Transmission Control Protocol)は、コネクション型で信頼性の高いバイトストリームベースの通信プロトコルです。データは送信前に接続し、送信後に切断する必要があります。クライアントはデータを送受信していますconnect()関数を使用してサーバーとの接続を確立する前。接続を確立する目的は、IPアドレス、ポート、物理リンクなどが正しいことを確認し、データ送信用のチャネルを開くことです。
接続を確立すると、TCPは3つのデータパケットを送信します。これは、一般に3ウェイハンドシェイク(Three-way Handshaking)と呼ばれています。画像は次のダイアログに例えることができます:

[Shake 1] 套接字A:“你好,套接字B,我这里有数据要传送给你,建立连接吧。”
[Shake 2] 套接字B:“好的,我这边已准备就绪。”
[Shake 3] 套接字A:“谢谢你受理我的请求。

4.2 TCPのスティッキーパケット問題とデータのボーダレス性:

https://blog.csdn.net/m0_37947204/article/details/80490512

4.3、TCPデータグラム構造:

影付きのフィールドを強調表示する必要があります:
(1)シーケンス番号:Seq(シーケンス番号)シーケンス番号は32ビットを占め、コンピューターAからコンピューターBに送信されるデータパケットのシーケンス番号を識別するために使用されます。これは、コンピューターがデータを送信するときにマークされます。
(2)確認番号:Ack(確認番号)確認番号は32ビットを占め、クライアントとサーバーの両方が送信できます。Ack= Seq + 1。
(3)フラグビット:各フラグビットは1ビットを占有します。URG、ACK、PSH、RST、SYN、FINの合計6つがあり、具体的な意味は次のとおりです。

(1)URG:紧急指针(urgent pointer)有效。
(2)ACK:确认序号有效。
(3)PSH:接收方应该尽快将这个报文交给应用层。
(4)RST:重置连接。
(5)SYN:建立一个新连接。
(6)FIN:断开一个连接。

4.4。接続の確立(3ウェイハンドシェイク):

connect()を使用して接続を確立すると、クライアントとサーバーは互いに3つのデータパケットを送信します。次の図を参照してください。

クライアントは、接続が確立されていないため、ソケットはCLOSED状態になって、後にソケットを作成するソケット()関数を呼び出し、サーバー側の呼び出しは(聞く)関数の後、にソケットがLISTEN状態、クライアントの要求をリッスンを開始
クライアント端末が要求を開始します
  。1)クライアントがconnect()関数を呼び出すと、TCPプロトコルはデータパケットを作成し、SYNフラグビットを設定します。これは、データパケットが同期接続の確立に使用されていることを示します。同時に、乱数1000が生成され、「Seq」フィールドにデータパケットのシーケンス番号が表示されます。これらのタスクが完了すると、サーバーへのデータパケットの送信が開始され、クライアントはSYN-SEND状態になります。
  2)サーバーはデータパケットを受信し、SYNフラグが設定されていることを検出し、これが接続を確立するためにクライアントから送信された「要求パケット」であることを認識します。サーバーはデータパケットもセットアップし、SYNフラグとACKフラグを設定します。SYNは、データパケットが接続の確立に使用されていることを示します。ACKは、クライアントから送信されたデータパケットが受信されたことを確認するために使用され
  ますサーバーは乱数2000を生成し、「シリアル番号」を入力します(Seq)「フィールド。2000はクライアントパケットとは関係ありません。
  サーバーは、クライアントデータパケットのシーケンス番号(1000)に1を加算して1001を取得し、[Ack]フィールドにこの番号を入力します。
  サーバーはデータパケットを送信し、SYN-RECV状態に入ります
  。3)クライアントはデータパケットを受信し、SYNおよびACKフラグが設定されていることを検出し、これがサーバーから送信された「確認パケット」であることを認識します。クライアントは "Ack"(Ack)フィールドをチェックして、その値が1000 + 1であるかどうかを確認します。そうである場合、接続が正常に確立されたことを意味します。
  次に、クライアントは引き続きデータパケットを構築し、ACKフラグを設定します。これは、クライアントがサーバーから「確認応答パケット」を正しく受信したことを示します。同時に、サーバーから送信されたデータパケットのシーケンス番号(2000)に1を追加して2001を取得し、この番号を使用して[Ack]フィールドに入力します。
  クライアントはデータパケットを送信し、ESTABLISED状態になり、接続が正常に確立されたことを示します。
  4)サーバーはデータパケットを受信し、ACKフラグが設定されていることを検出し、これがクライアントから送信された「確認応答パケット」であることを認識します。サーバーは、「確認番号(Ack)」フィールドをチェックして、その値が2000 + 1であるかどうかを確認します。その場合、接続が正常に確立され、サーバーはESTABLISED状態になります。
  この時点で、クライアントとサーバーはESTABLISED状態になり、接続が正常に確立され、データを送受信できます。

4.5、切断するTCP 4ウェイハンドシェイク

接続を確立することは非常に重要です。それはデータの正しい送信の前提です。切断も重要です。これにより、コンピュータは使用されなくなったリソースを解放できます。接続を正常に切断できない場合、データ転送エラーが発生するだけでなく、ソケットが閉じられず、リソースを占有し続けることになります。同時実行の量が多い場合、サーバーのプレッシャーが気になります。
切断には4つのハンドシェイクが必要です。これは次のダイアログに例えることができます。

[Shake 1] 套接字A:“任务处理完毕,我希望断开连接。”
[Shake 2] 套接字B:“哦,是吗?请稍等,我准备一下。”
等待片刻后……
[Shake 3] 套接字B:“我准备好了,可以断开连接了。”
[Shake 4] 套接字A:“好的,谢谢合作。”

次の図は、クライアントがアクティブに切断するシナリオを示しています。

接続が確立されると、クライアントとサーバーの両方がESTABLISED状態になります。このとき、クライアントは切断要求を開始します。

  1. クライアントがclose()関数を呼び出した後、FINパケットをサーバーに送信し、FIN_WAIT_1状態に入ります。FINは、Finishの省略形です。つまり、タスクを完了するには切断が必要です。
  2. データパケットを受信した後、サーバーはFINフラグが設定されていることを検出し、切断することを認識しているため、「確認パケット」をクライアントに送信し、CLOSE_WAIT状態に入ります。
    注:サーバーはリクエストを受け取った後すぐには切断しませんが、最初に「確認パケット」をクライアントに送信し、切断する準備をする必要があることを知らせます。
  3. 「確認パケット」を受信した後、クライアントはFIN_WAIT_2状態に入り、サーバーがデータパケットを再度送信する準備をするのを待ちます。
  4. しばらく待った後、サーバーは切断する準備ができているので、FINパケットをクライアントにアクティブに送信して、準備ができていることを通知し、切断します。次に、LAST_ACK状態に入ります。
  5. サーバーからFINパケットを受信すると、クライアントはACKパケットをサーバーに送信して、サーバーに切断するように伝えます。次に、TIME_WAIT状態に入ります。
  6. クライアントのACKパケットを受信すると、サーバーは切断し、ソケットを閉じて、CLOSED状態になります。

4.6。TIME_WAITステータスについての説明

クライアントが最後にACKパケットを送信した後、直接CLOSED状態に入って接続を閉じるのではなく、TIME_WAIT状態に入ります。

TCPはコネクション型の転送方式です。データが損失やエラーなしにターゲットマシンに正しく到達できることを確認する必要があります。ネットワークは不安定で、いつでもデータを破壊する可能性があるため、マシンAがマシンBにデータパケットを送信するたびに、マシンBは「確認応答」してACKパケットを返し、マシンAに受信したことを伝えます。これにより、マシンAはデータ転送が成功したことを知ることができます。マシンBがACKパケットを返さない場合、マシンAはマシンBがACKパケットを返すまで再送信します。

クライアントが最後にACKパケットをサーバーに送信すると、サーバーはネットワークの問題のためにそれを受信しない場合があります。サーバーはFINパケットを再度送信します。この時点でクライアントが接続を完全に閉じた場合、サーバーはACKを受信しません。パッケージ化するため、クライアントは少し待って、CLOSED状態に入る前に相手がACKパケットを受信することを確認する必要があります。では、どれくらい待つ必要がありますか?

データパケットにはネットワーク内の存続時間があります。この時間が経過してもターゲットホストに到達しない場合、データパケットは破棄され、ソースホストに通知されます。これは最大セグメント寿命(MSL)と呼ばれます。TIME_WAITは、CLOSED状態に入る前に2MSL待機します。ACKパケットがサーバーに到達するまでにMSL時間、サーバーがFINパケットを再送信するまでにMSL時間かかります。2MSLは、データパケットのラウンドトリップの最大時間です。2MSLの後でサーバーによって再送信されたFINパケットが受信されない場合、サーバーがACKパケットを受信したことを意味します。

4.7エレガントな切断-シャットダウン()

close()/ closesocket()とshutdown()の違い
正確には、close()/ closesocket()を使用してソケットを閉じ、メモリからソケット記述子(またはハンドル)をクリアし、その後は使用できなくなりますこのソケットは、C言語のfclose()に似ています。アプリケーションがソケットを閉じると、ソケットに関連付けられている接続とキャッシュは意味を失い、TCPプロトコルは自動的に操作をトリガーして接続を閉じます。

shutdown()は、ソケットではなく接続を閉じるために使用されます。shutdown()が何回呼び出されても、ソケットはメモリからソケットをクリアするためにclose()/ closesocket()が呼び出されるまで存在します。
close()/ closesocket()を呼び出してソケットを閉じるか、shutdown()を呼び出して出力ストリームを閉じると、FINパケットが相手に送信されます。FINパケットはデータ送信が完了したことを示し、コンピュータはFINパケットを受信し、これ以上データが送信されないことを認識しています。

デフォルトでは、close()/ closesocket()は、出力バッファーにデータがあるかどうかに関係なく、すぐにFINパケットをネットワークに送信し、shutdown()は、FINパケットを送信する前に出力バッファーのデータが送信されるのを待ちます。つまり、close()/ closesocket()を呼び出すと、出力バッファー内のデータが失われますが、shutdown()を呼び出すと、

5. OSIモデル

TCP / IPは、OSIのネットワークモデルレイヤーを次のように分割します。

TCP / IPプロトコル参照モデルは、すべてのTCP / IPシリーズプロトコルを4つの抽象層に分類します。
アプリケーション層:TFTP、HTTP、SNMP、FTP、SMTP、DNS、Telnetなど。
トランスポート層:TCP、UDP
ネットワーク層:IP 、ICMP、OSPF、EIGRP、IGMP
データリンク層:SLIP、CSLIP、PPP、MTU。
各抽象化層は、下位層によって提供されるサービスに基づいて構築され、上位層にサービスを提供します。

6.ソケットの一般的に使用される関数インターフェイスとその原理

グラフィックソケット機能:

6.1。socket()関数を使用してソケットを作成する

int socket(int af, int type, int protocol);
  1. afはアドレスファミリ(アドレスファミリ)です。つまり、一般的に使用されるIPアドレスのタイプはAF_INETおよびAF_INET6です。AFは「Address Family」の略で、INETは「Inetnet」の略です。AF_INETは127.0.0.1などのIPv4アドレスを表し、AF_INET6は1030 :: C9B4:FF12:48AA:1A2BなどのIPv6アドレスを表します。
    127.0.0.1を覚えておく必要があります。これは、次のチュートリアルで頻繁に使用されるローカルアドレスを意味する特別なIPアドレスです。
  2. typeはデータ送信方法であり、一般的に使用されるのはSOCK_STREAMおよびSOCK_DGRAMです
  3. protocolは、送信プロトコルを表します。一般的に使用されるのはIPPROTO_TCPとIPPTOTO_UDPで、それぞれTCP送信プロトコルとUDP送信プロトコルを表します

6.2。bind()およびconnect()関数を使用する

socket()関数を使用してソケットを作成し、ソケットのさまざまなプロパティを決定した後、サーバー側のbind()関数を使用してソケットを特定のIPアドレスとポートバインドします。 IPアドレスとポートのデータは、処理のためにソケットにのみ渡すことができます。クライアントは、接続()関数を使用して接続を確立する必要があります

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  

sockはソケットファイル記述子、addrはsockaddr構造体変数のポインター、addrlenはaddr変数のサイズ、
次のコードはsizeof()で計算して、作成されたソケットをIPアドレス127.0.0.1とポート1234 バインドできます。セット:

//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//创建sockaddr_in结构体变量
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(1234);  //端口
//将套接字和IP、端口绑定
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

connect()関数は、接続を確立するために使用されます。そのプロトタイプは次のとおりです。

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); 

6.3。listen()およびaccept()関数を使用する

サーバー側プログラムでは、bind()を使用してソケットをバインドした後、listen()関数を使用してソケットをパッシブリスニング状態にしてから、accept()関数を呼び出す必要があります。これにより、いつでもクライアントのリクエストに応答できます。
** listen()関数**を介して、ソケットをパッシブリスニング状態にすることができます。そのプロトタイプは次のとおりです。

int listen(int sock, int backlog); 

sockはリスニング状態に入る必要があるソケットであり、バックログは要求キューの最大長です。
いわゆるパッシブモニタリングとは、クライアント要求がない場合、ソケットは「スリープ」状態にあり、クライアント要求を受信した場合にのみ、ソケットが「覚醒」して要求に応答することを意味します。

要求キュー
ソケットがクライアント要求を処理しているときに、新しい要求が入った場合、ソケットは処理できず、バッファーにのみ入れることができます。処理のために読み上げてください。新しいリクエストが引き続き発生する場合は、バッファーがいっぱいになるまで、順番にバッファーのキューに入れられます。このバッファはリクエストキューと呼ばれます。

バッファの長さ(クライアントリクエストをいくつ保存できるか)は、listen()関数のbacklogパラメータで指定できますが、その長さの基準はなく、必要に応じて決定でき、同時実行数は10または20です。 。

バックログの値がSOMAXCONNに設定されている場合、要求キューの長さはシステムによって決定されます。この値は一般に大きく、数百以上になることがあります。

リクエストキューがいっぱいになると、新しい

リクエストキューがいっぱいになると、新しいリクエストは受信されません。Linuxの場合、クライアントはECONNREFUSEDエラーを受信します

注:listen()は、ソケットをlisten状態にするだけで、要求を受け取りません。リクエストを受け取るには、accept()関数が必要です。

ソケットがリスニング状態にある場合クライアント要求accept()関数介して受信できます。そのプロトタイプは次のとおりです。

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen); 

そのパラメーターはlisten()およびconnect()と同じです。sockはサーバー側ソケット、addrはsockaddr_in構造体変数、addrlenはパラメーターaddrの長さで、sizeof()によって取得できます。

accept()はクライアントと通信するための新しいソケットを返し、addrはクライアントのIPアドレスとポート番号を保存し、sockはサーバー側のソケットです。区別するために誰もが注意を払う必要があります。後でクライアントと通信するときは、元のサーバー側ソケットの代わりに、この新しく生成されたソケットを使用します。

最後に、listen()はソケットをリスニング状態にするだけで、実際にはクライアント要求を受信しないことに注意してください。listen()の背後にあるコードは、accept()が見つかるまで実行を続けます。accept()は、新しい要求が到着するまでプログラムの実行をブロックします(背後のコードは実行できません)。

6.4。ソケットデータの受信と送信


Linuxでのデータの送受信Linuxはソケットファイルと通常のファイルを区別しません。ソケットにデータを書き込むにはwrite()を使用し、ソケットからデータを読み取るにはread()を使用します。

前に述べたように、2台のコンピューター間の通信は2つのソケット間の通信に相当します。サーバー側のソケットにデータを書き込むためにWrite()を使用し、クライアントはそれを受信して​​、読み取りを使用できます。 ()ソケットから読み取り、通信を完了します。
write()のプロトタイプは次のとおりです。

ssize_t write(int fd, const void *buf, size_t nbytes);

fdは書き込むファイルの記述子、bufは書き込むデータのバッファアドレス、nbytesは書き込むデータのバイト数です。
write()関数は、バッファbufのnbytesバイトをファイルfdに書き込みます。成功すると、書き込まれたバイト数が返され、失敗すると-1が返されます。
read()のプロトタイプは次のとおりです。

ssize_t read(int fd, void *buf, size_t nbytes);

fdは読み取るファイルの記述子、bufは受信するデータのバッファアドレス、nbytesは読み取るデータのバイト数です。

read()関数は、fdファイルからnbytesバイトを読み取り、それらをバッファーbufに保存します。成功すると、読み取られたバイト数が返されます(ただし、ファイルの最後では0が返されます)。失敗すると-1が返されます。

6.5、ソケットバッファとブロッキングモード

ソケットバッファー
各ソケットが作成されると、入力バッファーと出力バッファーの2つのバッファーが割り当てられます。

write()/ send()はすぐにデータをネットワークに送信しませんが、最初にデータをバッファーに書き込み、次にTCPプロトコルがデータをバッファーからターゲットマシンに送信します。データがバッファーに書き込まれると、関数は正常に戻ることができます。ターゲットマシンに到達したかどうか、ネットワークに送信されたタイミングに関係なく、これらはTCPプロトコルの役割です。

TCPプロトコルは、書き込み()/送信()関数とは独立しています。データは、バッファに書き込まれるとすぐにネットワークに送信されるか、バッファにバックログし続ける場合があります。複数回書き込まれたデータは、一度にネットワークに送信されます。それは、その時のネットワーク状態、現在のスレッドがアイドルかどうかなど、多くの要因に依存し、プログラマーによって制御されません。

同じことがread()/ recv()関数にも当てはまり、ネットワークから直接ではなく入力バッファーからデータを読み取ります

これらのI / Oバッファーの特性は、次のように構成できます。

(1)I/O缓冲区在每个TCP套接字中单独存在;
(2)I/O缓冲区在创建套接字时自动生成;
(3)即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
(4)关闭套接字将丢失输入缓冲区中的数据

入力および出力バッファーのデフォルトのサイズは、通常8Kです。これは、getsockopt()関数を通じて取得できます。

unsigned optVal;
int optLen = sizeof(int);
getsockopt(servSock, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen);
printf("Buffer length: %d\n", optVal);

ブロックモード
TCPソケットの場合(デフォルト)、データの送信に書き込み()/送信()を使用する場合:

1) 首先会检查缓冲区,如果缓冲区的可用空间长度小于要发送的数据,那么 write()/send() 会被阻塞(暂停执行),直到缓冲区中的数据被发送到目标机器,腾出足够的空间,才唤醒 write()/send() 函数继续写入数据。
2) 如果TCP协议正在向网络发送数据,那么输出缓冲区会被锁定,不允许写入,write()/send() 也会被阻塞,直到数据发送完毕缓冲区解锁,write()/send() 才会被唤醒。
3) 如果要写入的数据大于缓冲区的最大长度,那么将分批写入。
4) 直到所有数据被写入缓冲区 write()/send() 才能返回。

read()/ recv()を使用してデータを読み取る場合:

1) 首先会检查缓冲区,如果缓冲区中有数据,那么就读取,否则函数会被阻塞,直到网络上有数据到来。
2) 如果要读取的数据长度小于缓冲区中的数据长度,那么就不能一次性将缓冲区中的所有数据读出,剩余数据将不断积压,直到有 read()/recv() 函数再次读取。
3) 直到读取到数据后 read()/recv() 函数才会返回,否则就一直被阻塞。
这就是TCP套接字的阻塞模式。所谓阻塞,就是上一步动作没有完成,下一步动作将暂停,直到上一步动作完成后才能继续,以保持同步性。

TCPソケットはデフォルトでブロッキングモードです

おすすめ

転載: www.cnblogs.com/zhangchaocoming/p/12699662.html