Linux マルチスレッド同時実行ソケットサーバーの実装 (11) -【Linux 通信アーキテクチャシリーズ】

シリーズ記事ディレクトリ

C++ スキル シリーズ
Linux 通信アーキテクチャ シリーズ
C++ 高性能最適化プログラミング シリーズ
ソフトウェア アーキテクチャの深い理解設計シリーズ
高度な C++ 同時スレッド プログラミング
設計パターン シリーズ

ご注目をお待ちしております!
ここに画像の説明を挿入

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

1. Linux スレッドの概要

  • (1)头文件
    #include < pthread.h >

  • (2)创建线函数
    int pthread_create( pthread_t * 制限thread, const pthread_attr_t * 制限attr, void* (* start_routine)(void *) , void * 制限arg);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    thread : 新しく作成されたスレッドの ID を保持する変数のアドレス。異なるスレッドを区別します;
    attr: スレッド属性を渡すために使用されるパラメータ、NULL を渡す場合、デフォルト属性を持つスレッドを作成します;
    start_routine: スレッドの main 関数に相当し、別のスレッドで実行される関数のアドレス値 (関数ポインタ)実行フロー ;
    arg : 3 番目の関数を通じて関数を呼び出すときに、パラメーター情報を含む変数アドレス値を渡します。
    pthread_join 関数を呼び出す - それを集計します (スレッドが終了するのを待ちます)
    int pthread_join(pthread_t スレッド、void ** ステータス);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    – thread : パラメータ値 ID が終了したスレッドがこの関数から戻ります
    – status : スレッドの main 関数の戻り値のポインタ変数アドレス値を保存します。
    pthread_detach 関数を呼び出します - 関数から切り離します
    int pthread_detach(pthread_t スレッド);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    – スレッドの終了時に破棄する必要があるスレッドの ID。

  • (3)互斥量
    int pthread_mutex_init(pthread_mutex_t * ミューテックス, const pthread_mutex * attr);
    int pthread_mutex_destroy(pthread_mutex_t * ミューテックス);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    – mutex ミューテックス作成時はミューテックスを保持する変数のアドレス値を渡し、破棄時は破棄するミューテックスのアドレス値を渡す – attr 作成するミューテックスの属性を渡し、指定する必要がある特別な属性がない場合は、NULLを渡します。

    int pthread_mutex_lock(pthred_mutex_t * ミューテックス);
    int pthread_mutex_unlock(pthread_mutex_t * ミューテックス);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    ミューテックスロック。

  • (4)信号量
    #include< セマフォ.h >
    int sem_init (sem_t * sem、int pshared、unsigned int 値);
    int sem_destroy (sem_t *sem);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    – sem: シグナルの作成時にセマフォを保持する変数のアドレス値を渡し、破棄されるときに破棄する必要があるセマフォ変数のアドレス値を渡す – pshared: 他の値を渡すときに、セマフォを作成
    する複数のプロセスで共有可能; 0を渡すと、内部的に1つのプロセスのみが使用できるセマフォを作成;
    – value : 新しく作成されるセマフォの初期値を指定します。
    int sem_post (sem_t *sem);
    int sem_wait (sem_t *sem);
    成功した場合は 0 を返し、失敗した場合は別の値を返します。
    – sem : セマフォの読み出し値を保存する変数アドレス値を転送します sem_post に渡すとセマフォは 1 増加します sem_wait に渡すとセマフォは 1 減少します 信号値は 0 未満にすることはできません。セマフォが 0 のときに sem_wait 関数を呼び出すと、スレッドはブロック状態になります。

第二に、マルチスレッド同時サーバーの実現

以下に紹介するのは、複数のクライアント間で情報を交換できる簡単なチャット プログラム (厳密には商用コードではありません) です。

chart_server.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<errno.h>
#include<pthread.h>
#define BUF_SIZE 100
#define MAX_CLNT 256

void * handle_clnt(void * arg);
void send_msg(char * msg, int len);
void error_handling(char * msg);
int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutex;

int main(int argc, char *argv[])
{
    
    
	//用来管理接入的客户端套接字的变量和数组。访问这两个变量的代码将构成临界区
	int serv_sock, clnt_sock; 
	struct sockaddr_in serv_adr, clnt_adr;
	int clnt_adr_sz;
	pthread_t t_id;
	if(argc != 2){
    
    
		printf("Usage : %s <port> \n", argv[0]);
		exit(1);
	}

	pthread_mutex_init(&mutex, NULL);
	serv_sock = socket(PF_INET, SOCK_STREAM, 0);
	
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	if(bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)
		error_handling("bind() error");
	if(listen(serv_sock, 5) == -1)
		error_handling("listen() error");
	
	while(1)
	{
    
    
		clnt_adr_sz = sizeof(clnt_adr);
		clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);

		pthread_mutex_lock(&mutex);
		//每当有新的连接时,将相关信息写入变量clnt_cnt和clnt_socks
		clnt_socks[clnt_cnt++] = clnt_sock;
		pthread_mutex_unlock(&mutex);

		//创建线程向新连入的客户端提供服务
		pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);
		//调用pthread_detach函数从内存中完全销毁已终止的线程
		pthread_detach(t_id);
		printf("Connected client IP: %s \n", inet_ntoa(clnt_adr.sin_addr));
	}
	close(serv_sock);
	return 0;
}

void * handle_clnt(void * arg)
{
    
    
	int clnt_sock = *((int*)arg);
	int str_len = 0, i;
	char msg[BUF_SIZE];

	while((str_len = read(clnt_sock, msg, sizeof(msg))) != 0)
		send_msg(msg, str_len);
	
	pthread_mutex_lock(&mutex);
	for(i = 0; i < clnt_cnt; i++) //remove disconnected client
	{
    
    
		if(clnt_sock == clnt_socks[i])
		{
    
    
			while(i++ < clnt_cnt - 1)
				clnt_socks[i] = clnt_socks[i + 1];
			break;
		}
	}
	clnt_cnt--;
	pthread_mutex_unlock(&mutex);
	close(clnt_sock);
	return NULL;
}

//该函数负责向所有客户端发送消息
void send_msg(char *msg, int len) // send to all
{
    
    
	int i;
	pthread_mutex_lock(&mutex);
	for(i = 0; i < clnt_cnt; i++)
		write(clnt_socks[i], msg, len);
	pthread_mutex_unlock(&mutex);
}

void error_handling(char *msg)
{
    
    
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

チャット_clnt.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<errno.h>
#include<pthread.h>
#define BUF_SIZE 100
#define NAME_SIZE 20

void * send_msg(void *arg);
void * recv_msg(void *arg);
void error_handling(char *msg);

char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];

int main(int argc, char *argv[])
{
    
    
	int sock;
	struct sockaddr_in serv_addr;
	pthread_t snd_thread, rcv_thread;
	void *thread_return;
	if(argc != 4){
    
    
		printf("Usage : %s <IP> <port> <name> \n", argv[0]);
		exit(1);
	}
	
	sprintf(name, "[%s]", argv[3]);
	sock = socket(PF_INET, SOCK_STREAM, 0);

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_addr.sin_port = htons(atoi(argv[2]));

	if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("connect() error");
	
	pthread_create(&snd_thread, NULL, send_msg, (void *)&sock);
	pthread_create(&rcv_thread, NULL, recv_msg, (void *)&sock);
	pthread_join(snd_thread, &thread_return);
	pthread_join(rcv_thread, &thread_return);
	close(sock);
	return 0;
}

void *send_msg(void *arg) //send thread main
{
    
    
	int sock = *((int *)arg);
	char name_msg[NAME_SIZE + BUF_SIZE];
	while(1)
	{
    
    
		fgets(msg, BUF_SIZE, stdin);
		if(!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
		{
    
    
			close(sock);
			exit(0);
		}
		sprintf(name_msg, "%s %s", name, msg);
		write(sock, name_msg, strlen(name_msg));
	}
	return NULL;
}

void *recv_msg(void * arg) //read thread main
{
    
    
	int sock = *((int *) arg);
	char name_msg[NAME_SIZE + BUF_SIZE];
	int str_len;
	while(1)
	{
    
    
		str_len = read(sock, name_msg, NAME_SIZE + BUF_SIZE - 1);
		if(str_len == -1)
			return (void *) - 1;
		name_msg[str_len] = 0;
		fputs(name_msg, stdout);
	}
	return NULL;
}

void error_handling(char *msg)
{
    
    
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

操作の結果は次のようになります。
ここに画像の説明を挿入

図1_1 チャットルームの実行結果

おすすめ

転載: blog.csdn.net/weixin_30197685/article/details/131947713