エントリーから実際の戦闘の基本までのTCPネットワークプログラミングモデル、シングルサーバーシングルユーザー非並行バージョン


序文

  • この記事は、学校でネットワークプログラミングを学んだ後、使い方がわからない場合にのみ使用できます。この章から得られるのは、最も基本的なTCPモデルの習得です。

プログラミングモデル図:


まず、ネットワークプログラミングの実践に必要な基本的な知識

  • IPアドレス(32ビットアドレス):ネットワークアドレス+ホスト番号構成:ホストを一意に識別できます。以前のネットワークアドレスは、同じネットワークセグメントの下にあるかどうかを判断するためのものです。同じネットワークセグメントの下で、異なるコンピュータを区別するために、IPアドレスが使用されます。ただし、IPアドレスは必ずしも永久に一意であるとは限りません。?????ネットワーク接続のアドレスに基づいて、ネットワークはさまざまな場所で接続され、ネットワークが再接続されるたびに、IPアドレスが異なる場合があります...コアは、IPがネットワークに接続するために必要な物理アドレスではないということです。。                                                                                                                      
  • macアドレス:上記のIPアドレスと比較すると、macアドレスは常に一意に決定される物理アドレスであり、IDカードと同等であり、世界中で一意に決定されます。異なるネットワークセグメントのネットワーク接続が変更されます。       
  • ポート番号(ポート)(16ビット) :プロセスを一意に識別します。ここでは、ポート番号が必要な理由について説明します。同じホスト上の2つのプロセス間で通信接続を確立する必要がある場合、または異なるホスト上の異なるプロセス間で接続を決定する必要がある場合、一意に作成することは不可能であるため、現時点ではIPアドレスだけで十分ではありません。プロセスを決定します。...。                                                                                                         
  • ネットワーク接続を確立する目的は何ですか、本質は何ですか?     通常のデータ通信を実行する場合、ネットワークサーバーとネットワーククライアントは、ポート番号を使用して独自のプロセスを一意に識別する必要があります...
  • 一意のプロセスの識別  :同じOS内で、プロセスをポート番号にバインドできます。ポート番号は、ネットワークレベルでホスト上の一意のプロセスを一意に識別します。   
  • ip + port    :ネットワーク全体で一意のプロセスを識別します     
  • ソケットの理解:英語の意味:ソケット、私の簡単な理解、サーバー側とクライアント側で同時に穴を開けると、この穴を通してデータの送受信が実現されるので、理解してください。仮想ネットワークケーブルを入手するのと同じように、一方の端をクライアントに接続し、もう一方の端をサーバーに接続してから通信します。.......ポートを開き、ポートからデータを読み取り、ポートにデータを送信します。
  • ソケット作成の詳細な分析 :IP層(ネットワーク層):IPV4またはIPV6のどちらを使用するか、トランスポート層にTCPまたはUDPを使用するか(この記事ではTCPのみを調査)TCPには、IPV4(AF_INET)+ TCP(SOCK_STREAM)を使用します)。
  • データ伝送のネットワーク伝送ストレージ形式が統一されています:マシンにはビッグエンディアンマシンとスモールエンディアンマシンの違いがありますビッグエンディアンマシンとリトルエンディアンマシンを区別するには、ローアドレスがローバイトを格納するかどうかを確認しますデータまたはハイバイトデータ、およびローアドレスはローバイトデータを格納します。セクションデータ(リトルエンディアンストレージ)、ローバイトストレージハイバイトデータ(ビッグエンディアンストレージ)。これは、異なるマシンが異なる形式でデータを格納するためですが、ネットワーク上のこれらのデータのストレージ形式を正しく定義するにはどうすればよいですか?????   
  • 解決策:固定ネットワークバイトオーダーに統合:ネットワークバイトオーダーは、ビッグエンディアンストレージ形式に従ってデータストリームのアドレス情報を送信するため、ビッグエンディアンマシンの場合、データを変換せずに直接送信できます。フォーマット。データストリームアドレス情報を決定するには、エンドマシンをネットワークバイトオーダーに変換して渡す必要があります。
  • 次のコードコメントは理解に役立ちます。h:ホスト(ホスト)n:ネットワーク(ネットワーク)l:長い(32ビットIPアドレス)s:短い(16ビット
  • ポート)   
  • SYNOPSIS
           #include <arpa/inet.h>
    
           uint32_t htonl(uint32_t hostlong);
    
           uint16_t htons(uint16_t hostshort);
    
           uint32_t ntohl(uint32_t netlong);
    
           uint16_t ntohs(uint16_t netshort);
    
    DESCRIPTION
           The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
    
           The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
    
           The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
    
           The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.
    
           On  the  i386 the host byte order is Least Significant Byte first, whereas the network byte order, as used on the Internet, is Most Significant
           Byte first.
    

  • 次に、sockaddr構造のグラフィカル分析を実行します   
  • structsockaddr_inの構造と分析は次のとおりです。    
  1.  sin_family:プロトコルファミリ(ドメイン)は、IPV4ネットワークプロトコルを表すためにAF_INETを直接書き込みます   
  2.  sin_port:16ビットのポート番号(バイトオーダーに注意)
  3.  sin_addr:32ビットIPアドレス(バイト順序に注意)  

次に、システムコールメソッドの分析

1.ソケット

  •  returnval   :成功すると、新しいソケットのファイル記述子が返されます。エラーの場合、-1が返され、errnoが適切に設定されます。成功するとソケットファイル記述子が返され、失敗すると-1が返されます。

2.バインド

 構造固有のパラメータ設定分析

INADDR_ANY   :これは基本的にマクロであり、サーバーに複数のネットワークカードがあり、各ネットワークカードが複数のIPアドレスにバインドされている可能性があるため、任意のローカルIPアドレスを表すことができます。特定のクライアントが接続を確立すると、使用するIPアドレスが決定されます(率直に言って、単一のマシンで複数のIPによって引き起こされるIP設定エラーの問題を回避するためにワイルドカードサーバーで可能なIPアドレスです)

return val:成功した場合は0を返し、失敗した場合は-1を返します。

3.聞く

 return val:成功した場合は0を返し、失敗した場合は-1を返し、着信sockfdがリスニングソケットになります。

リスニングソケット(listenfd)  :世界で唯一のリスニングソケットはサーバー上にあります。リスニングソケットは世界で唯一のものであり、常にリスニング状態にあることに特に注意してください(connfd:connectfdとは異なります)。 :次のテキストの接続ソケット)

接続ソケット(connfd)    :接続が確立されるたびに新しいconnfdが生成されます。

実例では、リスニングソケットと接続ソケットを理解しています。これらはサービスソケットとも呼ばれます。

たとえば、高級ホテルでは、ドアに車を迎えるウェイターが常にいて、このウェイターがドアを監視していて、ゲストが到着すると招待されます。入った後、別のウェイターがサービスを提供します。このホテルは比較的ハイエンドです。入店後、ウェイターは常にサービスを調整しています。ここに来るすべての人は、出発するまでサービスを調整するために新しいサービスソケット(接続ソケット)が必要です。ドア、それは常に一定です。どのクライアントが来ているのか、どのクライアントが仕事への接続を確立したいのか...。

4.受け入れる

コードモデル:

while(1){

        clientaddr_len = sizeof(clientaddr);

        connfd = accept(listenfd、(SA *)&clientaddr、&clientaddr_len);

        サービスロジックコード

        close(connfd);//サービスが終了して接続を閉じます。

 return val:戻り値は正確に上記の   connfdであり、ソケットを接続します。接続が実現されるたびに、新しいconnfdが返されるため、accept関数は通常、クライアントの接続要求を継続的に受信するために無限ループに配置されます。サービスに合わせて調整...

5.接続する

return val:成功した場合は0を返し、失敗した場合は-1を返します

ヘルパー関数:ポストシーケンスは、文字列IPアドレスとsin_addrの間の変換を使用する必要があります。 


3.実際のケースコード

1.サーバー側

コードは次のように表示されます。

[tangyujie@VM-4-9-centos Serve]$ cat server.c
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>

#define SERVE_PORT 12345
#define ERR_EXIT(m) \
	do { perror(m); exit(EXIT_FAILURE); } while (0)
typedef struct sockaddr SA;

int listenfd;								//设置全局监听套接字, 方便关闭
void handle(int signo) {
	fprintf(stdout, "ByeBye!\n");
	close(listenfd);
	exit(EXIT_SUCCESS);
}

int main() {
	signal(SIGINT, handle);
	int connfd;
	struct sockaddr_in serveAdd, clientAdd;
	socklen_t clientAdd_len;
	char ipbuff[256];
	//创建套接字
	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		ERR_EXIT("socket");	//协议家族 服务类型(套接字类型), 协议弃用(0)
	}
	//确定服务端地址簇
	bzero(&serveAdd, sizeof(serveAdd));					//清0
	serveAdd.sin_family = AF_INET;
	serveAdd.sin_port = htons(SERVE_PORT);
	serveAdd.sin_addr.s_addr = htonl(INADDR_ANY);		//注意转网络字节序
	//bind 端口 地址信息
	if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
		ERR_EXIT("bind");
	}
	//开始监听..
	if (listen(listenfd, 3) == -1) {
		ERR_EXIT("listen");
	}
	printf("Accepting connections..\n");
	//循环不断的接收客户的连接请求进行服务
	while (1) {
		clientAdd_len = sizeof(clientAdd);
		if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
			ERR_EXIT("accept");
		}
		printf("recieve connection from ip is %s and port is %d\n",
			inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)), 
			ntohs(clientAdd.sin_port));
		//服务
		while (1) {
			char buff[1024] = {0};
			int i;
			int n = read(connfd, buff, sizeof(buff));		//读数据
			if (n == -1) {
				ERR_EXIT("read");
			} 
			if (n == 0) {									//说明客户端主动断开连接
				break;
			}
			//处理数据, 简单的小写字符转大写
			for (i = 0; i < n; ++i) {
				buff[i] = toupper(buff[i]);
			}
			//写回
			write(connfd, buff, n);
		}
    fprintf(stdout, "ip %s and port is %d interrupt connfd\n",
        ipbuff, ntohs(clientAdd.sin_port));
		close(connfd);										//服务结束断开连接
	}
    return 0;
}

2.クライアント

コードは以下のように表示されます:

[tangyujie@VM-4-9-centos Serve]$ cat client.c
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#include <string.h>

#define SERVE_PORT 12345								//端口号
#define ERR_EXIT(m)\
	do { perror(m); exit(EXIT_FAILURE); } while (0)     //错误处理
typedef struct sockaddr SA;								
int sockfd;												//设置全局, 方便关闭
void handle(int signo) {
	fprintf(stdout, "ByeBye!\n");
	close(sockfd);
	exit(EXIT_SUCCESS);
}
int main(int argc, char* argv[]) {
	if (argc != 2) {
		fprintf(stderr, "%s <ip>", argv[0]);
		close(EXIT_FAILURE);
	}
	signal(SIGINT, handle);
	struct sockaddr_in serveAdd;
	//创建套接字
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		ERR_EXIT("socket");	//协议家族 服务类型(套接字类型), 协议弃用(0)
	}
		//确定服务端地址簇
	bzero(&serveAdd, sizeof(serveAdd));					//清0
	serveAdd.sin_family = AF_INET;
	serveAdd.sin_port = htons(SERVE_PORT);
	//将传入的ip字符串转换为 sin_addr
	if (inet_pton(AF_INET, argv[1], &serveAdd.sin_addr) == -1) {
		ERR_EXIT("inet_ntop");
	} 
	//不需要绑定端口号 系统随机分配一个临时端口号, 直接连接
	if (connect(sockfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
		ERR_EXIT("connect");
	}
	while (1) {										//死循环, 使用ctrl c信号关闭连接
		char buff[1024];
		printf("请说>>");
		scanf("%s", buff);
		write(sockfd, buff, strlen(buff));
		int n = read(sockfd, buff, sizeof(buff));
		if (n == -1) {
			ERR_EXIT("read");
		}
		buff[n] = 0;
		fprintf(stdout, ">>%s\n", buff);
	}
}

シーケンスの2番目の部分:同時ブログリンクの解決:https ://blog.csdn.net/weixin_53695360/article/details/122790450?spm = 1001.2014.3001.5502

まとめ(この記事にはインターネット上でいくつかの参照がありますが、基本的に私自身の理解から、欠陥があります。誰もが一緒に修正し、学び、進歩するのを助けることができることを願っています)

上記を介して:

私たちは主に、ネットワークのIPアドレスとMACアドレスの比較研究と意味を習得する必要があります。

Macアドレスは、世界で唯一のフィールドに設定されています。

 IPアドレスはネットワーク接続に基づいています。IPアドレスは、ネットワークセグメントごとに異なる場合や、同じネットワークセグメント内で異なる時間に異なる場合があります。IPアドレスは、ネットワーク番号+ホスト番号で構成され、ホストを一意に識別できます。 ...。

ポート番号は、同じホスト上の異なるプロセスを区別する必要があるため、主にネットワーク全体に固有のプロセスを識別するためのものです。

ソケット接続サービスの本質:ネットワーク全体の2つの異なるプロセス間のネットワークベースの相互通信です。この通信方法はソケット通信と呼ばれ、同じホスト間の通信の制限を打破することが本質です。異なるホスト間の通信を実現します。通信

次に、最も基本的なネットワーク通信のTCPモデルがあります...

上記のモデルでは、いくつかの問題があります。1つのクライアントだけがサーバーに接続する場合は問題がないことがわかりましたが、2番目のクライアントが接続して要求する場合、それを提供する方法はありません???????? ?

これは私たちの生活の実際のニーズと一致していません。つまり、複数のクライアントに同時にサービスを提供する方法はありません...この問題が発生し、それを解決する方法については、コメント領域で話し合うことができます

おすすめ

転載: blog.csdn.net/weixin_53695360/article/details/122754482