Linux ネットワークプログラミング機能の簡単な分析

Linux ネットワークプログラミング機能の簡単な分析

ネットワーク プログラミングの基本的な機能: これは、TCP プロトコル通信を実現するための基本的なステップでもあります。実装コードは最後にあります。通信するには、IP を独自の IP に変更する必要があります。

1.ソケットを作成する

関数プロトタイプ:

#include <sys/types.h>
#include <sys/socket.h>
int ソケット(int ドメイン、int タイプ、int プロトコル);

パラメータリスト:

ドメイン パラメータには次の値があります。

AF_INET: IPv4 プロトコル
AF_INET6: IPv6 プロトコル
AF_LOCAL: Unixドメインプロトコル
AF_ROUTE: ルーティングソケット
AF_KEY: キーソケット

タイプの値:

SOCKET_STREAM: 双方向の信頼性の高いデータストリーム(TCPに対応)
SOCKET_DGRAM: 双方向の信頼性のないデータグラム、UDPに対応
SOCKET_RAW: トランスポート層以下のプロトコルを提供し、ICMPメッセージの送受信などの内部ネットワークインターフェースにアクセスできます。

プロトコル値:

タイプがSOCKET_RAWの場合、プロトコルタイプを示すためにこの値を設定する必要があります。他のタイプの場合は、0に設定してください。

この関数は、指定された形式のソケットを作成し、その記述子を返します。成功した場合は記述子が返され、失敗した場合は -1 が返されます。

2. ソケットをバインドする

関数プロトタイプ:

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

パラメータリスト:

sockfdは以前に作成されたソケット記述子です

my_addr は、一般的なソケット構造体へのポインタです。TCP プロトコル プログラミングを行う場合、通常は sockaddr_in 構造体が使用されます。

構造の内容は次のとおりです。

構造体 socketaddr_in
{
   unsigned short int sin_family; //対応するアドレスファミリIP v4のAF_INTEを入力します
   uint16_t sin_port; //対応するポート番号 struct in_addr sin_addr; //対応するIPアドレス unsigned char sin_zero[8];
};
構造体 in_addr
{
   uint32_t s_addr;
};

addrlen は上記の構造体のサイズであり、sizeof を使用して取得できます。

bind 関数を使用する前に、sockaddr_in タイプの構造体を作成し、その構造体にサーバー情報を保存し、作成したソケットをその構造体にバインドする必要があります。成功した場合は 0 を返し、失敗した場合は -1 を返します。

ポート番号と IP を設定するときは、まず構造体をクリアします。メイン関数がパラメータを渡す場合、対応するポート番号と IP は文字列形式であるため、関数を使用して変換する必要があります。変換形式は次のとおりです。

char port[]="8888"
文字ip[]="192.168.1.1"
構造体 sockaddr_in seraddr'
seraddr.sin_port = htos(atoi(ポート))
seraddr.sin_addr.s_addr = inet_addr(ip);

3. 聞き手を作る。聞く

関数プロトタイプ:

int listen(int fd, int バックログ);

パラメータリスト:

fd は監視対象のソケット記述子です。backlog は監視キューのサイズです。

(1)listen実行後、ソケットはパッシブモードになります。

(2)キューがいっぱいになると、新たな接続要求は拒否されます。クライアントは接続エラー WSAECONNREFUSED を受け取ります。

(3)すでにlisten中のソケットに対してlistenを実行しても効果はありません。

4. 接続が受け入れられるのを待つ

関数プロトタイプ:

#include <sys/socket.h>
 int accept(int s, struct sockaddr * addr, int * addrlen);

bind 関数を比較すると、2 つのパラメータはほぼ同じですが、accept の addr は const によって変更されません。つまり、addr は接続されたクライアントのアドレス情報を保存するために使用され、Yangsaddlen 時に返される addr のサイズです。

したがって、accept 関数は接続されたクライアントのファイル記述子を返し、クライアントのアドレス情報を新しい sockaddr_in 構造体に保存します。接続が失敗した場合は -1 を返します。

5. メッセージの送受信 send and recv

関数プロトタイプ

 int send( SOCKET s, const char FAR *buf, int len, int flags );
int recv( SOCKET s, char FAR *buf, int len, int flags);

この関数のパラメータは次のとおりです。

  • 最初のパラメータは送信側/受信側のソケット記述子を指定します。
  • 2 番目のパラメータは、アプリケーションから送信されるデータを格納するバッファを指定します。
  • 3 番目のパラメータは、実際に送受信されるデータのバイト数を示します。
  • 4 番目のパラメータは通常 0 に設定されます。

送信のプロセス

ここでは、同期ソケットの送信関数の実行フローについてのみ説明します。この関数を呼び出すとき、最初にソケットの送信バッファーの長さで送信されるデータの長さを比較しますSの送信バッファーにデータを送信し始めていないか、Sの送信バッファーには、Sの送信バッファーをレンと比較します。 、プロトコルは、sの送信バッファーの残りのスペースにbufのデータをコピーするだけです。

send 関数がデータを正常にコピーすると、コピーされた実際のバイト数を返します。データのコピー中にエラーが発生した場合、send は SOCKET_ERROR を返します。send がプロトコルによるデータ送信を待機している間にネットワークが切断された場合も、send 関数は SOCKET_ERROR を返します。

送信関数は、buf 内のデータを s の送信バッファの残りのスペースに正常にコピーした後に戻りますが、この時点ではデータが接続のもう一方の端にすぐに送信されない可能性があることに注意してください。プロトコルの後続の送信プロセス中にネットワーク エラーが発生した場合、次の Socket 関数は SOCKET_ERROR を返します。 (send を除くすべての Socket 関数は、ソケットの送信バッファ内のデータがプロトコルによって送信されるまで待機してから、処理を続行する必要があります。待機中にネットワーク エラーが発生した場合、Socket 関数は SOCKET_ERROR を返します)。

recvのプロセス:

ここでは同期ソケットのrecv関数の実行フローについてのみ説明します。アプリケーションが recv 関数を呼び出すと、recv はまず、s の送信バッファ内のデータがプロトコルによって送信されるのを待機します。プロトコルが s の送信バッファ内のデータを送信するときにネットワーク エラーが発生した場合、recv 関数は SOCKET_ERROR を返します。s の送信バッファにデータがない場合、またはプロトコルによってデータが正常に送信された場合、recv はまずソケット s の受信バッファをチェックします。s の受信バッファにデータがない場合、またはプロトコルがデータを受信して​​いる場合、recv はプロトコルがデータを受信するまで待機します。プロトコルがデータの受信を完了すると、recv 関数は s の受信バッファ内のデータを buf にコピーします (プロトコルが受信したデータは buf の長さよりも大きい場合があるため、この場合、s の受信バッファ内のすべてのデータをコピーするには、recv 関数を複数回呼び出す必要があります。recv 関数はデータをコピーするだけで、実際のデータ受信はプロトコルによって完了します)。recv 関数は、実際にコピーされたバイト数を返します。コピー中に recv が失敗した場合は、SOCKET_ERROR を返します。recv 関数がプロトコルによるデータ受信を待機している間にネットワークが中断された場合は、0 を返します。

TCP プロトコル自体は信頼性が高いですが、TCP を使用してデータを送信するアプリケーションが必ずしも信頼できるというわけではありません。ブロックされているかどうかに関係なく、送信サイズは相手側が受信するデータの量を表すものではありません。

ブロッキング モードでは、送信関数のプロセスは、アプリケーションが要求したデータを送信バッファにコピーし、送信して確認後に戻ることです。ただし、送信バッファが存在するため、次のように表されます。送信バッファのサイズが要求されたサイズより大きい場合、送信関数はすぐに戻り、同時にデータをネットワークに送信します。それ以外の場合、送信はバッファに収容できない部分のデータをネットワークに送信し、相手側の確認を待ってから戻ります (受信側が受信バッファでデータを受信すると確認し、アプリケーションが recv を呼び出すのを待つ必要はありません)。

非ブロッキング モードでは、送信関数はデータをプロトコル スタックのバッファにコピーするだけです。バッファ内の使用可能なスペースが不十分な場合は、可能な限りコピーし、成功したコピーのサイズを返します。バッファ内の使用可能なスペースが 0 の場合は、-1 を返し、errno を EAGAIN に設定します。

6.ソケット記述子を閉じる

関数:

閉じる(sockfd);

ファイル操作と同様に、ソケットもファイルであり、使用後は閉じる必要があります。

7. TCPプロトコルに基づくC/Sサーバーモデル

Linux 学習ノート - ネットワーク プログラミング (パート 2)

図解TCPモデル

8. 実装コード

サーバ:

#include <stdio.h>
#include <stdlib.h>
#include <文字列.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
 
typedef struct sockaddr_in SIN;
typedef struct sockaddr SA;
 
int main(int argc,char *argv[])
{
    SIN セラッドル;
    SIN クリアアドレス;
    int len=sizeof(SIN);
    //リスニングソケットを作成します int lisfd=socket(AF_INET,SOCK_STREAM,0);
    if(lisfd<0)
    {
        perror("ソケット");
        終了(0);
    }
    printf("ソケット%dが正常に作成されました\n",lisfd);
    bzero(&seraddr,sizeof(seraddr));
    seraddr.sin_family=AF_INET;
    seraddr.sin_port = htons(8888);
    seraddr.sin_addr.s_addr = inet_addr("192.168.1.6");
    //ソケットをバインド int ret=bind(lisfd,(SA*)(&seraddr),len);
    戻り値<0の場合
    {
        perror("バインド");
        終了(0);
    }
    printf("バインドが成功しました\n");
    //リスニングを開始 ret=listen(lisfd,1024);
    戻り値<0の場合
    {
        perror("聞く");
        終了(0);
    }
    printf("監視は成功しました\n");
    //接続を待機し、接続されたソケット情報を保存します int clifd=accept(lisfd,(SA*)(&cliaddr),(socklen_t *)(&len));
    if(clifd<0)
    {
        perror("受け入れる");
        終了(0);
    }
    printf("クライアント %d が正常に接続されました\n",clifd);
    //読み取りと書き込み char readbuf[1024]={0};
    char送信バッファ[1024]={0};
    ながら(1)
    {
        recv(clifd、readbuf、sizeof(readbuf)、0);
        printf("recv:%s\n",readbuf);
        fgets(sendbuf,sizeof(sendbuf),stdin);
        送信(clifd、送信バッファ、サイズ(送信バッファ)、0);
    }
    //ソケットを閉じます close(clifd);
    閉じる(lisfd);
    0を返します。
}

クライアント:

#include <stdio.h>
#include <stdlib.h>
#include <文字列.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
 
typedef struct sockaddr_in SIN;
typedef struct sockaddr SA;
 
int main(int argc,char *argv[])
{
    SIN セラッドル;
    //リスニングソケットを作成します int serfd=socket(AF_INET,SOCK_STREAM,0);
    if(serfd<0)
    {
        perror("ソケット");
        終了(0);
    }
    printf("ソケット%dが正常に作成されました\n",serfd);
    bzero(&seraddr,sizeof(seraddr));
    seraddr.sin_family=AF_INET;
    seraddr.sin_port = htons(8888);
    seraddr.sin_addr.s_addr = inet_addr("192.168.1.6");
    //接続を要求 int ret=connect(serfd,(SA*)(&seraddr),sizeof(SIN));
    戻り値 == -1 の場合
    {
        perror("接続");
        終了(0);
    }
    printf("接続に成功しました\n");
    //読み取りと書き込み char senbuf[1024]={0};
    char readbuf[1024]={0};
    ながら(1)
    {
        fgets(senbuf,sizeof(senbuf),stdin);
        送信(serfd、senbuf、サイズ(senbuf)、0);
        recv(serfd、readbuf、sizeof(readbuf)、0);
        printf("recv:%s\n",readbuf);
    }
    //ソケットを閉じます close(serfd);
    0を返します。
}

Linux ネットワーク プログラミング機能の簡単な分析に関するこの記事はこれで終わりです。Linux ネットワーク プログラミング機能に関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Linux ネットワーク プログラミング: UDP に基づく信頼性の高いファイル転送の例
  • Linux ネットワーク プログラミング UDP ソケット プログラム例
  • Linux ネットワーク プログラミング ソケット ファイル転送の例
  • Linuxネットワークプログラミングで使用されるネットワーク関数の詳細な説明と使用例
  • Linuxネットワークプログラミングの基本機能を学ぶ

<<:  2つのウェブサイトページ翻訳プラグインの共有

>>:  MySQL でのサブクエリの基本的な使用法

推薦する

Docker で Confluence をデプロイする

1. 環境要件1. Docker 17以上がインストールされている2. コンテナ操作docker r...

Vueはビデオ再生を実装するためにビデオタグを使用します

この記事では、ビデオタグを使用してビデオ再生を実装するVueの具体的なコードを参考までに共有します。...

Linux Centos でスクリプトを使用して Docker をインストールする方法

Dockerの主な機能は何ですか?現在、Docker には少なくとも次のアプリケーション シナリオが...

mysql bin-log ログファイルを sql ファイルに変換する方法

mysqlbinlogのバージョンを表示mysqlbinlog -V [--version] bin...

Vue.js での VNode の使用

VNodeとはvue.js には VNode クラスがあり、これを使用してさまざまな種類の vnod...

MySQL での order by の使用に関する詳細

目次1. はじめに2. 本文2.1 単一列のソート2.2 複数の列を並べ替える2.3 ソート方法2....

MySQLでSELECT文が実行される仕組み

目次1. マクロの観点からMySQLを分析する2. SQL ステートメントを実行するには、どの程度の...

border-image を使用してテキストバブルの境界線を実装する方法のサンプルコード

開発中に、非常に単純なテキストバブル効果に遭遇しました。これは、おおよそ次のようになります。 うーん...

MySQL で指定エンコーディングを実装する際の落とし穴について

前面に書かれた環境: MySQL 5.7+、MySQL データベースの文字エンコードは utf8、テ...

Vue における $router と $route の違いの詳細な説明

通常、vue プロジェクトではルーティングを使用します。vue-router は vue.js の公...

Centos6.5 で MySQL 5.7.19 をインストールして設定する方法

Centos6.5にmysql5.7.19をインストールするための詳細な手順は次のとおりです。 1....

Docker コンテナのログを表示およびクリーンアップする方法 (テスト済みで効果的)

1. 問題Docker コンテナのログにより、ホストのディスク領域がいっぱいになりました。 doc...

モバイルレイアウトにvw+remを使用する方法

まだ rem フレキシブルレイアウトを使用していますか?圧縮された js コードの大きなセクションを...

MySQL MHA の高可用性構成とフェイルオーバーの詳細な導入手順

目次1. MHAの紹介1. MHAとは何ですか? 2. MHAの構成3. MHAの特徴2. MySQ...

Dockerコンテナの状態変換の実装

Dockerコンテナの状態遷移図2度目の実戦 [root@localhost ~]# docker ...