C 使用單向鏈表實現消息羣發功能

需求與思路

需求是爲了實現一個服務器N個客戶端通信,並且能夠實現線上羣發功能。
以前開發過類似的c\s通信,但都是點對點的實現通信,現在是1對N,剛開始有點頭疼,想的挺複雜。
但後來突然想到既然要實現通信,不如就先實現通信。
點對點通信很容易就實現了,就是一對一的socket,接下來就是一對多,
既然是一對多,那就使用線程來實現把,一個線程處理一個通信。
好,1對多有了,那怎麼在建立起通信後再次隨時發送消息呢,
注意這裏必須先讓客戶端進行連接才能通信,不然不知道對方的ip與端口想發消息也發不出去。

單鏈表

說實在的,C語言必須要自己造輪子,這一點是我經常吐槽的一個點。寫代碼的時候多希望用python代替啊,哈哈哈。話說回來,想要存儲必要的信息,並且後邊還需要遍歷查詢並且使用,我只想到了單鏈表(嗯,還有python 的 list 和dict),單純的看需求,想要記錄客戶端的信息單鏈表足夠了,那麼接下來造輪子吧。
//定義鏈表結構
struct LinkAddr
{
int data;
int server_msgfd;
struct sockaddr_in server_msg;
struct LinkAddr *next;
};
*很多人使用typedef 來定義這樣也可以。
//定義一個鏈表結構,我這裏使用的是全局的。
struct LinkAddr *link_glable;

//創建頭結點,並初始化。
void init()
{
link_glable = (struct LinkAddr*)malloc(sizeof(struct LinkAddr));
if(link_glable == NULL)
{
printf(“頭結點分配失敗,程序終止! \n”);
}
link_glable->data=0;
link_glable->next=NULL;
memset(link_glable->imei,0,20);
link_glable=link_glable;
}

//接下來是增加節點
void append(int sock)
{
struct LinkAddr *compare = link_glable;
while(compare)
{
if(compare->data ==0)
{
compare=compare->next;
continue;
}
if((compare->server_msgfd ==sock) && (strcmp(imei,compare->imei)==0))
return;
}

struct LinkAddr * pNew = (struct LinkAddr *)malloc(sizeof(struct LinkAddr));
pNew->next =link_glable;
pNew->server_msgfd=sock;
pNew->server_msg=client_addr;
pNew->data=1;
memset(pNew->imei,0,20);
strcpy(pNew->imei,imei);
link_glable=pNew;//把新建的節點的地址賦值給link_glable,現在的鏈表是這樣的:link_glable->head->null

}

多線程socket通信

創建線程池(參考https://blog.csdn.net/guotianqing/article/details/88929210)

thread_pool.h如下:
#include <pthread.h>

struct job {
void * (*callback_function)(void *arg);
void *arg;
struct job *next;
};

struct threadpool {
int thread_num;
int queue_max_num;
struct job *head;
struct job *tail;
pthread_t *pthreads;
pthread_mutex_t mutex;
pthread_cond_t queue_empty;
pthread_cond_t queue_not_empty;
pthread_cond_t queue_not_full;
int queue_cur_num;
int queue_close;
int pool_close;
};

struct threadpool *threadpool_init(int thread_num, int queue_max_num);

int threadpool_add_job(struct threadpool *pool, void *(*callback_function)(void *arg), void *arg);

int threadpool_destroy(struct threadpool *pool);

void *threadpool_function(void *arg);
————————————————
版權聲明:本文爲CSDN博主「guotianqing」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/guotianqing/article/details/88929210

thread_pool.c如下:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include “thread_pool.h”

struct threadpool *threadpool_init(int thread_num, int queue_max_num)
{
struct threadpool *pool = NULL;

do {
    pool = (struct threadpool *)calloc(1, sizeof(struct threadpool));
    if (!pool) {
        printf("calloc error: %m\n");
        break;
    }
    pool->thread_num = thread_num;
    pool->queue_max_num = queue_max_num;
    pool->queue_cur_num = 0;
    pool->head = NULL;
    pool->tail = NULL;
    if (pthread_mutex_init(&(pool->mutex), NULL)) {
        printf("init mutex error: %m\n");
        break;
    }
    if (pthread_cond_init(&(pool->queue_empty), NULL)) {
        printf("init queue_empty error: %m\n");
        break;
    }
    if (pthread_cond_init(&(pool->queue_not_empty), NULL)) {
        printf("init queue_not_empty error: %m\n");
        break;
    }
    if (pthread_cond_init(&(pool->queue_not_full), NULL)) {
        printf("init queue_not_full error: %m\n");
        break;
    }
    pool->pthreads = calloc(1, sizeof(pthread_t) * thread_num);
    if (!pool->pthreads) {
        printf("calloc pthreads error: %m\n");
        break;
    }
    pool->queue_close = 0;
    pool->pool_close = 0;
    int i;
    for (i = 0; i < pool->thread_num; i++) {
        pthread_create(&(pool->pthreads[i]), NULL, threadpool_function, (void *)pool);
    }
    return pool;
} while (0);

return NULL;

}

int threadpool_add_job(struct threadpool *pool, void *(*callback_function)(void *arg), void *arg)
{
assert(pool != NULL);
assert(callback_function != NULL);
assert(arg != NULL);

pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == pool->queue_max_num) && !(pool->queue_close || pool->pool_close)) {
    pthread_cond_wait(&(pool->queue_not_full), &(pool->mutex));
}
if (pool->queue_close || pool->pool_close) {
    pthread_mutex_unlock(&(pool->mutex));
    return -1;
}
struct job *pjob = (struct job*) calloc(1, sizeof(struct job));
if (!pjob) {
    pthread_mutex_unlock(&(pool->mutex));
    return -1;
}

pjob->callback_function = callback_function;
pjob->arg = arg;
pjob->next = NULL;
if (pool->head == NULL) {
    pool->head = pool->tail = pjob;
    pthread_cond_broadcast(&(pool->queue_not_empty));
} else {
    pool->tail->next = pjob;
    pool->tail = pjob;
}

pool->queue_cur_num++;
pthread_mutex_unlock(&(pool->mutex));

return 0;

}

void *threadpool_function(void *arg)
{
struct threadpool *pool = (struct threadpool *)arg;
struct job *pjob = NULL;

while (1) {
    pthread_mutex_lock(&(pool->mutex));
    while ((pool->queue_cur_num == 0) && !pool->pool_close) {
        pthread_cond_wait(&(pool->queue_not_empty), &(pool->mutex));
    }

    if (pool->pool_close) {
        pthread_mutex_unlock(&(pool->mutex));
        pthread_exit(NULL);
    }
    pool->queue_cur_num--;
    pjob = pool->head;
    if (pool->queue_cur_num == 0) {
        pool->head = pool->tail = NULL;
    } else {
        pool->head = pjob->next;
    }

    if (pool->queue_cur_num == 0) {
        pthread_cond_signal(&(pool->queue_empty));
    }
    if (pool->queue_cur_num == pool->queue_max_num - 1) {
        pthread_cond_broadcast(&(pool->queue_not_full));
    }
    pthread_mutex_unlock(&(pool->mutex));

    (*(pjob->callback_function))(pjob->arg);
    free(pjob);
    pjob = NULL;
}

}

int threadpool_destroy(struct threadpool *pool)
{
assert(pool != NULL);
pthread_mutex_lock(&(pool->mutex));
if (pool->queue_close || pool->pool_close) {
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pool->queue_close = 1;
while (pool->queue_cur_num != 0) {
pthread_cond_wait(&(pool->queue_empty), &(pool->mutex));
}
pool->pool_close = 1;
pthread_mutex_unlock(&(pool->mutex));
pthread_cond_broadcast(&(pool->queue_not_empty));
pthread_cond_broadcast(&(pool->queue_not_full));

int i;
for (i = 0; i < pool->thread_num; i++) {
    pthread_join(pool->pthreads[i], NULL);
}

pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthreads);

struct job *p;
while (pool->head != NULL) {
    p = pool->head;
    pool->head = p->next;
    free(p);
}
free(pool);

return 0;

}
————————————————
版權聲明:本文爲CSDN博主「guotianqing」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/guotianqing/article/details/88929210

定義全局sockaddr_in 變量
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
//init server的socket接口。return 什麼的自己定義就行
int init_sockfd()
{
if (( server_sockfd = socket( AF_INET , SOCK_STREAM , 0 )) < 0 )
{
printf(“Socket Failed !!!\r\n”);
event=V_SOCKET_ERROR ;
return(event);
}

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8901);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
 
	printf("bind Failed !!!\r\n");
	event=V_BIND_ERROR ;
	return(event);	 
}
if(listen(server_sockfd, 100) == -1)
{
	printf("listen Failed !!!\r\n");
	event=V_SOCKET_ERROR ;
	return(event);
}

event=V_OK;
return(event);

}

初始化完了接下來就是使用了。

主函數創建線程

int main(int argc, char **argv)
{
int flag = 1;
int client_sockfd=0;
struct threadpool *pool = threadpool_init(NUM_UNLIMIT,NUM_UNLIMIT);
init();
if(V_OK != init_sockfd())
{
printf(“init sock failed!\n”);
return -1;
}

while(1)
{

	switch (flag)
	{
		case PTHREADS_UNLIMIT:
			printf("PTHREADS_UNLIMIT......\n");
			int client_lenth=sizeof(client_addr);
			if ((client_sockfd = accept(server_sockfd, (struct sockaddr *) &client_addr, (socklen_t *) &client_lenth)) > 0)
			{
				
				printf("create pthread!\n");
				//pthread_create(&thread[pthread_count], NULL, responesEvent,);
				threadpool_add_job(pool, responesEvent, &client_sockfd);
				printf("wrisbands connect! \n");
			}				
			
			break;
		case PTHREAD_LIMIT:
			printf("PTHREAD_LIMIT......\n");
			break;
	}
}

}
//線程處理函數
void *responesEvent(void *client)
{
struct argvs * tmp =(struct argvs *)client;
int c_fd=tmp->tmp_fd;
unsigned char char_recv[MAX_BUF_LEN];

int byte=recv(c_fd,char_recv,MAX_BUF_LEN,0);
*
*處
*理
*過
*程
*
if(客戶端)
append(link_glable,c_fd,imei);
else
sendband()//其他處理(羣發消息機制)
}

void sendband()
{
struct LinkAddr send_link = link_glable;//鏈表地址賦值
while(send_link)
{
if(strstr(wris_id,send_link->imei) ==NULL )
{
send_link=send_link->next;
continue;
}
if(send_link->data ==0)
{
send_link=send_link->next;
continue;
}
connect(send_link->server_msgfd,(struct sockaddr
)&send_link->server_msg,sizeof(send_link->server_msg));
if(send(send_link->server_msgfd,back_buf,sizeof(back_buf),0) == 0)
{
printf(“send link send error!!!\n”);
return;
}
else
{
printf(“sand link send succ!!!\n”);
}

send_link=send_link->next;
}
}

參考鏈接

https://blog.csdn.net/guotianqing/article/details/88929210

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章