TCP/UDPに基づくソケットプログラミング

---- ソケットの概要:

ソケットはアプリケーション層とトランスポート層の間の抽象化層であり、TCP/IP 層の複雑な操作をいくつかの単純なインターフェイスに抽象化し、アプリケーション層がネットワーク内で通信するための実現されたプロセスを呼び出すことができます。

ソケットは UNIX から生まれました。すべてはファイルであるという Unix の哲学に基づいて、ソケットは「オープン、読み取り/書き込み、クローズ」モードの実装です。サーバーとクライアントはそれぞれ「ファイル」を維持します。接続が確立され、開かれた後、 , 相手が読み取れるように自分のファイルにコンテンツを書き込んだり、相手のコンテンツを読み取ったりして、通信が終了したらファイルを閉じることができます。

---- インターフェースの紹介:

socket(): ソケットを作成します

bind(): ソケットをローカルのアドレスとポートにバインドします。通常はサーバーによって呼び出されます。

listen(): TCP 専用、リスニング モードを有効にする

accept(): TCP 専用。サーバーはクライアントの接続を待ちます。通常はブロック状態です。

connect(): TCP 専用。クライアントはアクティブにサーバーに接続します。

send(): TCP専用、データを送信

recv(): TCP 専用、データを受信

sendto(): UDP 専用、指定された IP アドレスとポートにデータを送信します

recvfrom(): UDP 専用、データを受信し、データを返すリモート IP アドレスとポート

closesocket(): ソケットを閉じる

---- プロセスは次のとおりです。

インターフェイスについて詳しく説明します。一般的に使用されるシステム コールは次のとおりです。

>>socket() : ソケットの作成 

ソケットは通信エンドポイントを抽象化したものです。ファイル記述子を使用してファイルにアクセスするのと同じように、アプリケーションはソケット記述子を使用してソケットにアクセスします。ソケットを作成するには、socket() 関数を呼び出します。

原型:int ソケット(int ドメイン、int 型、int プロトコル);

戻り値: OK の場合はファイル (ソケット) 記述子を返し、エラーの場合は -1 を返します。

ドメイン:AF_INET、AF_INET6、AF_UNIX、AF_UNSPEC(アドレス形式)

タイプ:SOCK_DGRAM、SOCK_RAW、SOCK_STREAM、SOCK_SEQPACKET

プロトコル:IPPROTO_IP、IPPROTO_IPV6、IPPROTO_TCP、IPPROTO_UDP

通常、protocol 引数はゼロで、指定されたドメインとソケット タイプのデフォルト プロトコルを選択します。AF_INET 通信ドメインの SOCK_STREAM ソケットのデフォルトのプロトコルは TCP (Transmission Control Protocol) です。AF_INET 通信ドメインの SOCK_DGRAM ソケットのデフォルトのプロトコルは UDP (User Datagram Protocol) です。

注: UDP -- データグラム (データグラム)、コネクションレス、通信するための論理接続がピア間に存在しません。データグラムは自己完結型 (独立した) メッセージです。(類似の) 電子メール送信と同様に、複数の電子メールを送信できますが、電子メールが受信されること、および電子メールが受信される順序が保証されるわけではありません。各電子メールには受信者のアドレスが含まれています。

TCP -- バイト ストリーム 対照的に、バイト ストリーム (SOCK_STREAM) では、データを送信する前に接続を確立する必要があり、接続指向の通信は電話をかけるのと似ています。ポイントツーポイント接続には、送信元と宛先が含まれます。

ソケット上の通信は双方向です。シャットダウン機能を使用してソケットの I/O を無効にすることができます。

>> シャットダウン()   

原型:int shutdown(int sockfd, int how);

戻り値: OK の場合は 0、エラーの場合は -1 を返します。

shutdown() システム コールは、次のいずれかとして指定された how の値に応じて、ソケット sockfd の 1 つまたは両方のチャネルを閉じます。

方法: SHUT_RD を実行すると、ソケットからの読み取りが無効になります。SHUT_WR の場合、データ送信にソケットを使用できません。SHUT_RDWR を使用すると、データの送信と受信の両方を無効にすることができます。

shutdown() は、別の重要な点で close() と異なります。ソケットを参照する他のファイル記述子があるかどうかに関係なく、ソケット チャネルを閉じます。たとえば、sockfd は、接続されたストリーム ソケットを指します。次の呼び出しを行うと、接続は開いたままになり、ファイル記述子 fd2 を介して接続上で I/O を実行できます。

1. fd = dup(sockfd);

2. 閉じる(sockfd);

ただし、次の一連の呼び出しを行うと、接続の両方のチャネルが閉じられ、fd2 経由で I/O を実行できなくなります。

1. fd2 = dup(sockfd);

2. シャットダウン(sockfd,SHUT_RDWR);

shutdown() は、 SHUT_RDWR として指定されている場合でも、ファイル記述子を閉じないことに注意してください。ファイル記述子を閉じるには、さらに close() を呼び出す必要があります。

>>bind() : ソケットをアドレスにバインドします    

bind() システムコールはソケットをアドレスにバインドします。

原型:int binding(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

戻り値: 成功した場合は 0、エラーの場合は -1 を返します。

sockfd 引数は、socket() への以前の呼び出しから取得されたファイル記述子です。addr 引数は、このソケットがバインドされるアドレスを指定する構造体へのポインタです。この引数で渡される構造体のタイプはソケット ドメインによって異なります。addrlen 引数は、アドレス構造のサイズを指定します。

通常、サーバーのソケットを既知のアドレス、つまり、そのサーバーと通信する必要があるクライアント アプリケーションに事前に知られている固定アドレスにバインドします。

>> listen() : 着信接続をリッスンします    

原型:int listen(int sockfd, int backlog); // 成功した場合は 0 を返し、エラーの場合は -1 を返します。

listen() システムコールは、ファイル記述子 sockfd によって参照されるストリームソケットをパッシブとしてマークします。その後、ソケットは他の (アクティブな) ソケットからの接続を受け入れるために使用されます。

クライアントは、サーバーが accept() を呼び出す前に connect() を呼び出すことができます。これは、たとえば、サーバーが他のクライアントの処理でビジー状態であるために発生する可能性があります。これにより、図 56-2 に示すように、接続が保留状態になります。

カーネルは、後続の accept() を処理できるように、保留中の各接続要求に関する情報を記録する必要があります。backlog 引数を使用すると、そのような保留中の接続の数を制限できます。それ以降の接続要求は、保留中の接続が (accept() 経由で) 受け入れられるまでブロックされ、保留中の接続のキューから削除されます。

>> accept() : 接続を受け入れる   

accept() システムコールは、ファイル記述子 sockfd によって参照されるリスニングストリームソケット上の受信接続を受け入れます。accept() の呼び出し時に保留中の接続がない場合、sockfd がブロック モードのときに接続要求が到着するまで呼び出しはブロックされます。sockfd がノンブロッキング モードの場合、accept() は -1 を返し、errno を EAGAIN または EWOULDBLOCK に設定します。

原型:int accept(int sockfd, struct sockaddr * 制限 addr, socklen_t * 制限 len);

戻り値: OK の場合はファイル (ソケット) 記述子を返し、エラーの場合は -1 を返します。

この関数は、s の待機中の接続キューから最初の接続を抽出し、s と同じタイプの新しいソケットを作成し、ハンドルを返します。キュー内に待機中の接続がなく、ソケットがブロッキング モードにある場合、accept() は新しい接続が表示されるまで呼び出しプロセスをブロックします。ソケットが非ブロッキングで、キュー内に待機中の接続がない場合、accept() はエラー コード WSAEWOULDBLOCK を返します。接続を受け入れたソケットを新しい接続の受け入れに使用することはできず、元の待機ソケットは開いたままになります。

accept() について理解する重要な点は、新しいソケットが作成され、この新しいソケットは connect() を実行したピアソケットに接続されるということです。この新しいソケット記述子は、元のソケット (sockfd) と同じソケット タイプとアドレス ファミリを持ちます。接続されたソケットのファイル記述子は、accept() 呼び出しの関数結果として返されます。リスニングソケット (sockfd) は開いたままであり、さらなる接続を受け入れるために使用できます。一般的なサーバー アプリケーションは、リスニング ソケットを 1 つ作成し、それを既知のアドレスにバインドし、そのソケット経由で接続を受け入れることによってすべてのクライアント要求を処理します。

accept() の残りの引数は、ピアソケットのアドレスを返します (クライアント)。

クライアントの ID を気にしない場合は、addr パラメータと len パラメータを NULL に設定できます。それ以外の場合は、accept を呼び出す前に、addr (指向一バッファ) パラメータをアドレスを保持するのに十分な大きさのバッファに設定し、len が指す整数をバッファのサイズ (バイト単位) に設定する必要があります。戻ると、accept はクライアントのアドレスをバッファに書き込み、アドレスのサイズを反映するために len が指す整数を更新します。

>> connect() : ピアソケットに接続します  

原型:int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

戻り値: 成功した場合は 0、エラーの場合は -1 を返します。

connect() システムコールは、ファイル記述子 sockfd によって参照されるアクティブなソケットを、addr および addrlen によって指定されるアドレスを持つ listen ソケットに接続します。

>> send() : TCP タイプのデータを送信します 

原型:int send(int sockfd, const void * msg, int len, int flags);

各 TCP ソケットには送信バッファがあり、そのサイズは SO_SNDBUF オプションで変更できます。send 関数を呼び出すプロセスは、実際にはカーネルがユーザー データ (msg) を TCP ソケットの送信バッファにコピーするプロセスです。len が送信バッファのサイズより大きい場合は、-1 を返します。それ以外の場合は、バッファの残りのスペースが送信する len の長さを収容できるかどうかを確認し、そうでない場合は一部をコピーし、コピー長を返します (非ブロッキング送信 (送信をブロッキングしている場合) は、すべてのデータがバッファにコピーされるまで待機してから戻る必要があるため、ブロッキング送信の戻り値は len に等しくなければなりません)。バッファがいっぱいの場合は送信を待ち、空きができたらバッファにコピーします。コピー中にエラーが発生した場合は-1を返します。エラーの原因はerrnoの値を確認してください。

注: 送信が成功しても相手がデータを受信したことを意味するものではなく、その後のプロトコル送信中にネットワーク エラーが発生した場合、次の送信では送信エラー -1 が返されます。TCP によって相手に送信されたデータは、送信バッファ内のデータを削除する前に、相手によって確認される必要があります。それ以外の場合は、送信が成功するまでバッファにキャッシュされます。

パラメータの説明:

sockfd -- 送信側ソケット記述子 (非リスニング記述子)

msg -- 送信されるデータのバッファ (その内容の len の長さをソケットの送信バッファにコピーします)

len -- 送信されるデータのバイト長。

flags -- 通常は 0 に設定されます。

>>recv() : TCPタイプのデータ受信 

原型:int recv(int sockfd, void *buf, int len, unsigned int flags);

recv() は受信バッファからデータをコピーします。成功した場合はコピーされたバイト数を返し、失敗した場合は -1 を返します。ブロッキング モードでは、recv() はバッファーに少なくとも 1 バイトができるまでブロックしてから戻り、データがない場合はスリープします。ブロックされていない場合はすぐに戻り、データがあればコピーしたデータサイズを返し、そうでない場合はエラー -1 を返します。

パラメータの説明:

sockfd -- 受信側ソケット記述子 (非リスニング記述子)

buf -- 受信データのベースアドレス (ソケットの受信バッファの内容を buf にコピーします)

len -- 受信したデータのバイト数

flags -- 通常は 0 に設定されます。

>> sendto() : UDP タイプのデータを送信します 

原型:int sendto(int sockfd, const void * msg, int len, unsigned int flags, const struct sockaddr * dst_addr, int addrlen);

信頼性の低い接続 (UDP) のデータ送信に使用されます。UDP メソッドは接続ソケットを確立しないため、宛先ソケットのアドレスを指定する必要があります。

同じ UDP ソケット記述子 sockfd を使用して、異なる宛先アドレスと通信できます。ただし、TCP は事前に接続を確立する必要があり、接続ごとに異なるソケット記述子が生成され、これが反映されます。クライアントは接続に異なる fd を使用する必要があり、サーバーは受け入れるたびに異なる fd を生成します。

UDP には、信頼性の低い接続であるため、実際の送信バッファがありません。アプリケーション プロセスのデータ コピーを保存する必要はありません。アプリケーション プロセスのデータがプロトコル スタックに渡されると、データはプロトコル スタックにコピーされます。データリンク層でのデータ送信後、カーネルバッファ内のデータのコピーは削除されます。したがって、送信バッファは必要ありません。

sendto() の場合、 dest_addr および addrlen 引数は、データグラムの送信先のソケットを指定します。これらの引数は、connect() への対応する引数と同じ方法で使用されます。dest_addr 引数は、この通信ドメインに適したアドレス構造です。宛先ソケットのアドレスで初期化されます。addrlen 引数は、addr のサイズを指定します。

>>recvfrom() : UDPタイプのデータ受信 

原型:int recvfrom(int sockfd, void * buf, size_t len, int flags, struct sockaddr * src_addr, int * addrlen);

パラメータの説明:

sockfd -- 受信側のソケットの説明。

buf -- データを受信するためのアプリケーション バッファ アドレス。

len -- バッファ サイズを示します。

フラグ -- 通常は 0。

src_addr -- データ ソースのアドレス (IP アドレス、ポート番号)。

fromlen -- 入力として使用される場合、fromlen は通常 sizeof(struct sockaddr) に設定されます。

recvfrom() の場合、 src_addr および addrlen 引数は、データグラムの送信に使用されるリモート ソケットのアドレスを返します。(これらの引数は、接続しているピア ソケットのアドレスを返す accept() の addr 引数と addrlen 引数に似ています。) 呼び出し (在调用之前) の前に、addrlen は、 が指す構造体のサイズに初期化する必要があります。 src_addr(结构の大小); 戻ったとき(返されたとき)、この構造体に実際に書き込まれたバイト数が含まれます。

Embedded Internet of Things は多くのことを学ぶ必要があります。間違ったルートと内容を学習すると給与が上がります。

データ パッケージを全員で共有すると、約 150 G になります。その中の学習コンテンツ、対面聖典、プロジェクトは比較的新しく、完全なものです。(クリックすると、受け取る小さなアシスタントが見つかります)

おすすめ

転載: blog.csdn.net/m0_70911440/article/details/131548104