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