記事ディレクトリ
序文
この記事では、Linux ネットワーク プログラミングで高同時実行サーバーを使用する方法を説明します。まず、高同時実行サーバーとは何かを理解し、次に高同時実行サーバーの作成方法を学ぶ必要があります。
1. 高同時実行サーバーとは何ですか?
高同時実行性サーバーとは、同時に多数の同時リクエストを処理できるサーバー システムを指します。ネットワーク アプリケーションでは、複数のユーザーまたはクライアントが同時にサーバーを要求した場合、サーバーはこれらの要求を効率的に処理し、良好なパフォーマンスと安定性を維持できる必要があります。
高并发服务器的设计和实现需要考虑以下几个关键因素:
1. マルチスレッドまたはマルチプロセス処理: マルチスレッドまたはマルチプロセス方式を使用すると、サーバーが複数のリクエストを同時に処理できるようになります。各スレッドまたはプロセスは 1 つのリクエストの処理を担当するため、サーバーの同時処理能力が向上します。
2. 非同期ノンブロッキング I/O: 非同期ノンブロッキング I/O プログラミング モデルを使用すると、リクエスト処理中のスレッドやプロセスのブロックを回避し、システム リソースを最大限に活用し、サーバーの同時処理パフォーマンスを向上させることができます。一般的な方法は、Node.js のイベント駆動型 I/O、Nginx のイベント駆動型モデルなどのイベント駆動型プログラミング フレームワークまたはライブラリを使用することです。
3. ロードバランシング:サーバクラスタにロードバランサを導入し、リクエストを複数のサーバノードに分散することで、システム全体の同時処理能力をさらに向上させることができます。ロード バランサーは、特定の戦略 (ラウンド ロビン、重み付けなど) に従ってリクエストをさまざまなサーバーに分散し、各サーバーが適切な量のリクエストを処理できるようにします。
4. キャッシュと分散ストレージ: キャッシュと分散ストレージを適切に使用すると、サーバーの負荷が軽減され、応答速度が向上します。頻繁にアクセスされるデータをキャッシュに保存して、バックエンド ストレージ システムへの負担を軽減します。
5. 水平拡張: サーバーノードの追加やクラウドコンピューティングサービスプロバイダーのエラスティックスケーリング機能を使用するなど、サーバーの数を増やしてシステムの処理能力を拡張します。水平スケーリングにより、システムはより多くの同時リクエストを処理できるようになります。
2. マルチスレッドとマルチプロセスを使用して同時実行性の高いサーバーを実装するというアイデア
TCP 通信では、クライアントがサーバーに接続した後、サーバーは直接通信するのではなく、接続されたクライアントと通信するための新しいクライアントを作成します。このようにして、アイデアが得られました。サーバーを接続を待機する accpet 状態に保ち、新しいクライアントの接続を待つ必要があります。クライアントが接続すると、そのクライアントと通信するための新しいクライアントが作成されます。新しいクライアントからの接続要求を受信するサーバーの機能に影響を与えないように、このクライアント用のスレッドまたはプロセスを作成する必要があります。
3. マルチプロセスサーバーコードの作成
クライアントはサーバーへの接続に成功すると、fork 関数を使用してクライアントと通信するための子プロセスを作成し、親プロセスはシグナルを使用して子プロセスをリサイクルします。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
/*回收子进程*/
void catch_child(int sig)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
// 处理子进程的退出状态
}
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {
0};
int client = 0;
struct sockaddr_in caddr = {
0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {
0};
int r = 0;
pid_t pid;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 128) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while( 1 )
{
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
if (errno == EINTR)
{
// 信号中断,重新调用accept
client = accept(server, (struct sockaddr*)&caddr, &asize);
}
else
{
perror("accept");
printf("client accept error\n");
return -1;
}
}
pid = fork();//创建子进程与客户端进行通信
if(pid == 0)
{
close(server);
while (1)
{
/*子进程*/
len = read(client, buf, 1024);
if(len == 0)
{
printf("child exit\n");
close(client);
exit(1);//退出子进程
}
write(client, buf, len);
printf("recv_buf : %s len : %d\n", buf, len);
printf("child pid : %d\n", getpid());
}
}
else if(pid > 0)
{
/*父进程*/
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_child;
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
close(client);
}
}
close(server);
return 0;
}
4. マルチスレッドサーバーコードの作成
マルチスレッドを使用する方法は、マルチプロセスの方法よりも簡単です。ここでは、pthread_detach 関数を使用してスレッドを分離します。この方法では、手動でスレッドをリサイクルする必要はありません。スレッド関数でクライアントに接続して通信するだけです。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
void* client_work(void* arg)
{
int client = (int)arg;
int len = 0;
char buf[1024];
while (1)
{
len = read(client, buf, 1024);
if(len == 0)
{
printf("client is close\n");
return NULL;
}
printf("read buf : %s\n", buf);
write(client, buf, len);
}
close(client);
return NULL;
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {
0};
int client = 0;
struct sockaddr_in caddr = {
0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {
0};
int r = 0;
pid_t pid;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 128) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while( 1 )
{
pthread_t tid;
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
if (errno == EINTR)
{
// 信号中断,重新调用accept
client = accept(server, (struct sockaddr*)&caddr, &asize);
}
else
{
perror("accept");
printf("client accept error\n");
return -1;
}
}
pthread_create(&tid, NULL, client_work, (void*)client);
pthread_detach(tid);
}
close(server);
return 0;
}
要約する
この記事ではここで説明します。