Linux网络编程:线程池并发服务器 _UDP客户端和服务器_本地和网络套接字

文章目录:

一:线程池模块分析

threadpool.c

二:UDP通信

1.TCP通信和UDP通信各自的优缺点

2.UDP实现的C/S模型

server.c

client.c

三:套接字 

1.本地套接字

2.本地套 和 网络套对比

server.c

client.c


一:线程池模块分析


struct threadpool_t {pthread_mutex_t lock;               /* 用于锁住本结构体 */    pthread_mutex_t thread_counter;     /* 记录忙状态线程个数de琐 -- busy_thr_num */pthread_cond_t queue_not_full;      /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */pthread_cond_t queue_not_empty;     /* 任务队列里不为空时,通知等待任务的线程 */pthread_t *threads;                 /* 存放线程池中每个线程的tid。数组 */pthread_t adjust_tid;               /* 存管理线程tid */threadpool_task_t *task_queue;      /* 任务队列(数组首地址) */int min_thr_num;                    /* 线程池最小线程数 */int max_thr_num;                    /* 线程池最大线程数 */int live_thr_num;                   /* 当前存活线程个数 */int busy_thr_num;                   /* 忙状态线程个数 */int wait_exit_thr_num;              /* 要销毁的线程个数 */int queue_front;                    /* task_queue队头下标 */int queue_rear;                     /* task_queue队尾下标 */int queue_size;                     /* task_queue队中实际任务数 */int queue_max_size;                 /* task_queue队列可容纳任务数上限 */int shutdown;                       /* 标志位,线程池使用状态,true或false */
};typedef struct {void *(*function)(void *);          /* 函数指针,回调函数 */void *arg;                          /* 上面函数的参数 */} threadpool_task_t;                    /* 各子线程任务结构体 */rear = 5 % 5

线程池模块分析:1. main();		创建线程池。向线程池中添加任务。 借助回调处理任务。销毁线程池。2. pthreadpool_create();创建线程池结构体 指针。初始化线程池结构体 {  N 个成员变量 }创建 N 个任务线程。创建 1 个管理者线程。失败时,销毁开辟的所有空间。(释放)3. threadpool_thread()进入子线程回调函数。接收参数 void *arg  --》 pool 结构体加锁 --》lock --》 整个结构体锁判断条件变量 --》 wait  -------------------1704. adjust_thread()循环 10 s 执行一次。进入管理者线程回调函数接收参数 void *arg  --》 pool 结构体加锁 --》lock --》 整个结构体锁获取管理线程池要用的到 变量。	task_num, live_num, busy_num根据既定算法,使用上述3变量,判断是否应该 创建、销毁线程池中 指定步长的线程。5. threadpool_add ()总功能:模拟产生任务。   num[20]设置回调函数, 处理任务。  sleep(1) 代表处理完成。内部实现:加锁初始化 任务队列结构体成员。   回调函数 function, arg利用环形队列机制,实现添加任务。 借助队尾指针挪移 % 实现。唤醒阻塞在 条件变量上的线程。解锁6.  从 3. 中的wait之后继续执行,处理任务。加锁获取 任务处理回调函数,及参数利用环形队列机制,实现处理任务。 借助队头指针挪移 % 实现。唤醒阻塞在 条件变量 上的 server。解锁加锁 改忙线程数++解锁执行处理任务的线程加锁 改忙线程数——解锁7. 创建 销毁线程管理者线程根据 task_num, live_num, busy_num  根据既定算法,使用上述3变量,判断是否应该 创建、销毁线程池中 指定步长的线程。如果满足 创建条件pthread_create();   回调 任务线程函数。		live_num++如果满足 销毁条件wait_exit_thr_num = 10;  signal 给 阻塞在条件变量上的线程 发送 假条件满足信号    跳转至  --170 wait阻塞线程会被 假信号 唤醒。判断: wait_exit_thr_num  > 0 pthread_exit();  

threadpool.c

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "threadpool.h"#define DEFAULT_TIME 10                 /*10s检测一次*/
#define MIN_WAIT_TASK_NUM 10            /*如果queue_size > MIN_WAIT_TASK_NUM 添加新的线程到线程池*/ 
#define DEFAULT_THREAD_VARY 10          /*每次创建和销毁线程的个数*/
#define true 1
#define false 0typedef struct {void *(*function)(void *);          /* 函数指针,回调函数 */void *arg;                          /* 上面函数的参数 */
} threadpool_task_t;                    /* 各子线程任务结构体 *//* 描述线程池相关信息 */struct threadpool_t {pthread_mutex_t lock;               /* 用于锁住本结构体 */    pthread_mutex_t thread_counter;     /* 记录忙状态线程个数de琐 -- busy_thr_num */pthread_cond_t queue_not_full;      /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */pthread_cond_t queue_not_empty;     /* 任务队列里不为空时,通知等待任务的线程 */pthread_t *threads;                 /* 存放线程池中每个线程的tid。数组 */pthread_t adjust_tid;               /* 存管理线程tid */threadpool_task_t *task_queue;      /* 任务队列(数组首地址) */int min_thr_num;                    /* 线程池最小线程数 */int max_thr_num;                    /* 线程池最大线程数 */int live_thr_num;                   /* 当前存活线程个数 */int busy_thr_num;                   /* 忙状态线程个数 */int wait_exit_thr_num;              /* 要销毁的线程个数 */int queue_front;                    /* task_queue队头下标 */int queue_rear;                     /* task_queue队尾下标 */int queue_size;                     /* task_queue队中实际任务数 */int queue_max_size;                 /* task_queue队列可容纳任务数上限 */int shutdown;                       /* 标志位,线程池使用状态,true或false */
};void *threadpool_thread(void *threadpool);void *adjust_thread(void *threadpool);int is_thread_alive(pthread_t tid);
int threadpool_free(threadpool_t *pool);//threadpool_create(3,100,100);  
threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
{int i;threadpool_t *pool = NULL;          /* 线程池 结构体 */do {if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) {  printf("malloc threadpool fail");break;                                      /*跳出do while*/}pool->min_thr_num = min_thr_num;pool->max_thr_num = max_thr_num;pool->busy_thr_num = 0;pool->live_thr_num = min_thr_num;               /* 活着的线程数 初值=最小线程数 */pool->wait_exit_thr_num = 0;pool->queue_size = 0;                           /* 有0个产品 */pool->queue_max_size = queue_max_size;          /* 最大任务队列数 */pool->queue_front = 0;pool->queue_rear = 0;pool->shutdown = false;                         /* 不关闭线程池 *//* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*max_thr_num); if (pool->threads == NULL) {printf("malloc threads fail");break;}memset(pool->threads, 0, sizeof(pthread_t)*max_thr_num);/* 给 任务队列 开辟空间 */pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t)*queue_max_size);if (pool->task_queue == NULL) {printf("malloc task_queue fail");break;}/* 初始化互斥琐、条件变量 */if (pthread_mutex_init(&(pool->lock), NULL) != 0|| pthread_mutex_init(&(pool->thread_counter), NULL) != 0|| pthread_cond_init(&(pool->queue_not_empty), NULL) != 0|| pthread_cond_init(&(pool->queue_not_full), NULL) != 0){printf("init the lock or cond fail");break;}/* 启动 min_thr_num 个 work thread */for (i = 0; i < min_thr_num; i++) {pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);   /*pool指向当前线程池*/printf("start thread 0x%x...\n", (unsigned int)pool->threads[i]);}pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *)pool);     /* 创建管理者线程 */return pool;} while (0);threadpool_free(pool);      /* 前面代码调用失败时,释放poll存储空间 */return NULL;
}/* 向线程池中 添加一个任务 */
//threadpool_add(thp, process, (void*)&num[i]);   /* 向线程池中添加任务 process: 小写---->大写*/int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
{pthread_mutex_lock(&(pool->lock));/* ==为真,队列已经满, 调wait阻塞 */while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));}if (pool->shutdown) {pthread_cond_broadcast(&(pool->queue_not_empty));pthread_mutex_unlock(&(pool->lock));return 0;}/* 清空 工作线程 调用的回调函数 的参数arg */if (pool->task_queue[pool->queue_rear].arg != NULL) {pool->task_queue[pool->queue_rear].arg = NULL;}/*添加任务到任务队列里*/pool->task_queue[pool->queue_rear].function = function;pool->task_queue[pool->queue_rear].arg = arg;pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size;       /* 队尾指针移动, 模拟环形 */pool->queue_size++;/*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/pthread_cond_signal(&(pool->queue_not_empty));pthread_mutex_unlock(&(pool->lock));return 0;
}/* 线程池中各个工作线程 */
void *threadpool_thread(void *threadpool)
{threadpool_t *pool = (threadpool_t *)threadpool;threadpool_task_t task;while (true) {/* Lock must be taken to wait on conditional variable *//*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/pthread_mutex_lock(&(pool->lock));/*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/while ((pool->queue_size == 0) && (!pool->shutdown)) {  printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));/*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/if (pool->wait_exit_thr_num > 0) {pool->wait_exit_thr_num--;/*如果线程池里线程个数大于最小值时可以结束当前线程*/if (pool->live_thr_num > pool->min_thr_num) {printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());pool->live_thr_num--;pthread_mutex_unlock(&(pool->lock));pthread_exit(NULL);}}}/*如果指定了true,要关闭线程池里的每个线程,自行退出处理---销毁线程池*/if (pool->shutdown) {pthread_mutex_unlock(&(pool->lock));printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());pthread_detach(pthread_self());pthread_exit(NULL);     /* 线程自行结束 */}/*从任务队列里获取任务, 是一个出队操作*/task.function = pool->task_queue[pool->queue_front].function;task.arg = pool->task_queue[pool->queue_front].arg;pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;       /* 出队,模拟环形队列 */pool->queue_size--;/*通知可以有新的任务添加进来*/pthread_cond_broadcast(&(pool->queue_not_full));/*任务取出后,立即将 线程池琐 释放*/pthread_mutex_unlock(&(pool->lock));/*执行任务*/ printf("thread 0x%x start working\n", (unsigned int)pthread_self());pthread_mutex_lock(&(pool->thread_counter));                            /*忙状态线程数变量琐*/pool->busy_thr_num++;                                                   /*忙状态线程数+1*/pthread_mutex_unlock(&(pool->thread_counter));(*(task.function))(task.arg);                                           /*执行回调函数任务*///task.function(task.arg);                                              /*执行回调函数任务*//*任务结束处理*/ printf("thread 0x%x end working\n", (unsigned int)pthread_self());pthread_mutex_lock(&(pool->thread_counter));pool->busy_thr_num--;                                       /*处理掉一个任务,忙状态数线程数-1*/pthread_mutex_unlock(&(pool->thread_counter));}pthread_exit(NULL);
}/* 管理线程 */
void *adjust_thread(void *threadpool)
{int i;threadpool_t *pool = (threadpool_t *)threadpool;while (!pool->shutdown) {sleep(DEFAULT_TIME);                                    /*定时 对线程池管理*/pthread_mutex_lock(&(pool->lock));int queue_size = pool->queue_size;                      /* 关注 任务数 */int live_thr_num = pool->live_thr_num;                  /* 存活 线程数 */pthread_mutex_unlock(&(pool->lock));pthread_mutex_lock(&(pool->thread_counter));int busy_thr_num = pool->busy_thr_num;                  /* 忙着的线程数 */pthread_mutex_unlock(&(pool->thread_counter));/* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num) {pthread_mutex_lock(&(pool->lock));  int add = 0;/*一次增加 DEFAULT_THREAD 个线程*/for (i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY&& pool->live_thr_num < pool->max_thr_num; i++) {if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i])) {pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);add++;pool->live_thr_num++;}}pthread_mutex_unlock(&(pool->lock));}/* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/if ((busy_thr_num * 2) < live_thr_num  &&  live_thr_num > pool->min_thr_num) {/* 一次销毁DEFAULT_THREAD个线程, 隨機10個即可 */pthread_mutex_lock(&(pool->lock));pool->wait_exit_thr_num = DEFAULT_THREAD_VARY;      /* 要销毁的线程数 设置为10 */pthread_mutex_unlock(&(pool->lock));for (i = 0; i < DEFAULT_THREAD_VARY; i++) {/* 通知处在空闲状态的线程, 他们会自行终止*/pthread_cond_signal(&(pool->queue_not_empty));}}}return NULL;
}int threadpool_destroy(threadpool_t *pool)
{int i;if (pool == NULL) {return -1;}pool->shutdown = true;/*先销毁管理线程*/pthread_join(pool->adjust_tid, NULL);for (i = 0; i < pool->live_thr_num; i++) {/*通知所有的空闲线程*/pthread_cond_broadcast(&(pool->queue_not_empty));}for (i = 0; i < pool->live_thr_num; i++) {pthread_join(pool->threads[i], NULL);}threadpool_free(pool);return 0;
}int threadpool_free(threadpool_t *pool)
{if (pool == NULL) {return -1;}if (pool->task_queue) {free(pool->task_queue);}if (pool->threads) {free(pool->threads);pthread_mutex_lock(&(pool->lock));pthread_mutex_destroy(&(pool->lock));pthread_mutex_lock(&(pool->thread_counter));pthread_mutex_destroy(&(pool->thread_counter));pthread_cond_destroy(&(pool->queue_not_empty));pthread_cond_destroy(&(pool->queue_not_full));}free(pool);pool = NULL;return 0;
}int threadpool_all_threadnum(threadpool_t *pool)
{int all_threadnum = -1;                 // 总线程数pthread_mutex_lock(&(pool->lock));all_threadnum = pool->live_thr_num;     // 存活线程数pthread_mutex_unlock(&(pool->lock));return all_threadnum;
}int threadpool_busy_threadnum(threadpool_t *pool)
{int busy_threadnum = -1;                // 忙线程数pthread_mutex_lock(&(pool->thread_counter));busy_threadnum = pool->busy_thr_num;    pthread_mutex_unlock(&(pool->thread_counter));return busy_threadnum;
}int is_thread_alive(pthread_t tid)
{int kill_rc = pthread_kill(tid, 0);     //发0号信号,测试线程是否存活if (kill_rc == ESRCH) {return false;}return true;
}/*测试*/ #if 1/* 线程池中的线程,模拟处理业务 */
void *process(void *arg)
{printf("thread 0x%x working on task %d\n ",(unsigned int)pthread_self(),(int)arg);sleep(1);                           //模拟 小---大写printf("task %d is end\n",(int)arg);return NULL;
}int main(void)
{/*threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size);*/threadpool_t *thp = threadpool_create(3,100,100);   /*创建线程池,池里最小3个线程,最大100,队列最大100*/printf("pool inited");//int *num = (int *)malloc(sizeof(int)*20);int num[20], i;for (i = 0; i < 20; i++) {num[i] = i;printf("add task %d\n",i);/*int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg) */threadpool_add(thp, process, (void*)&num[i]);   /* 向线程池中添加任务 */}sleep(10);                                          /* 等子线程完成任务 */threadpool_destroy(thp);return 0;
}#endif

二:UDP通信

1.TCP通信和UDP通信各自的优缺点

TCP通信和UDP通信各自的优缺点:TCP:	面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。 丢包重传优点:稳定	数据流量稳定、速度稳定、顺序缺点:传输速度慢。相率低。开销大使用场景:数据的完整型要求较高,不追求效率大数据传输、文件传输UDP:	无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。 默认还原网络状况优点:传输速度块。相率高。开销小缺点:不稳定。数据流量。速度。顺序使用场景:对时效性要求较高场合。稳定性其次游戏、视频会议、视频电话。		腾讯、华为、阿里  ---  应用层数据校验协议,弥补udp的不足

2.UDP实现的C/S模型


UDP实现的 C/S 模型:recv()/send() 只能用于 TCP 通信。 替代 read、writeaccpet(); ---- Connect(); ---被舍弃server:lfd = socket(AF_INET, STREAM, 0);	SOCK_DGRAM --- 报式协议bind();listen();  --- 可有可无while(1){read(cfd, buf, sizeof) --- 被替换 --- recvfrom() --- 涵盖accept传出地址结构ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);sockfd: 套接字buf:缓冲区地址len:缓冲区大小flags: 0src_addr:(struct sockaddr *)&addr 传出。 对端地址结构addrlen:传入传出。返回值: 成功接收数据字节数。 失败:-1 errn。 0: 对端关闭。小-- 大write();--- 被替换 --- sendto()---- connectssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);sockfd: 套接字buf:存储数据的缓冲区len:数据长度flags: 0src_addr:(struct sockaddr *)&addr 传入。 目标地址结构addrlen:地址结构长度返回值:成功写出数据字节数。 失败 -1, errno		}close();client:connfd = socket(AF_INET, SOCK_DGRAM, 0);sendto(‘服务器的地址结构’, 地址结构大小)recvfrom()写到屏幕close();

server.c

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>#define SERV_PORT 8000int main(void)
{struct sockaddr_in serv_addr, clie_addr;socklen_t clie_addr_len;int sockfd;char buf[BUFSIZ];char str[INET_ADDRSTRLEN];int i, n;sockfd = socket(AF_INET, SOCK_DGRAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(SERV_PORT);bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));printf("Accepting connections ...\n");while (1) {clie_addr_len = sizeof(clie_addr);n = recvfrom(sockfd, buf, BUFSIZ,0, (struct sockaddr *)&clie_addr, &clie_addr_len);if (n == -1)perror("recvfrom error");printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),ntohs(clie_addr.sin_port));for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));if (n == -1)perror("sendto error");}close(sockfd);return 0;
}

client.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>#define SERV_PORT 8000int main(int argc, char *argv[])
{struct sockaddr_in servaddr;int sockfd, n;char buf[BUFSIZ];sockfd = socket(AF_INET, SOCK_DGRAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);//bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));        //无效while (fgets(buf, BUFSIZ, stdin) != NULL) {n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));if (n == -1)perror("sendto error");n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);         //NULL:不关心对端信息if (n == -1)perror("recvfrom error");write(STDOUT_FILENO, buf, n);}close(sockfd);return 0;
}

三:套接字 

1.本地套接字

本地套接字:IPC: pipe、fifo、mmap、信号、本地套(domain)--- CS模型对比网络编程 TCP C/S模型, 注意以下几点:1. int socket(int domain, int type, int protocol); domain:AF_INET 											--> AF_UNIX/AF_LOCAL type: SOCK_STREAM/SOCK_DGRAM  都可以2. 地址结构:  sockaddr_in 											--> sockaddr_unstruct sockaddr_in srv_addr; 									--> struct sockaddr_un srv_adrr;srv_addr.sin_family = AF_INET;  								--> srv_addr.sun_family = AF_UNIX;srv_addr.sin_port = htons(8888);   						    	strcpy(srv_addr.sun_path, "srv.socket")srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);					len = offsetof(struct sockaddr_un, sun_path) + strlen("srv.socket");bind(fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));  		--> bind(fd, (struct sockaddr *)&srv_addr, len); 3. bind()函数调用成功,会创建一个 socket。因此为保证bind成功,通常我们在 bind之前使用 unlink("srv.socket");4. 客户端不能依赖 “隐式绑定”。并且应该在通信建立过程中,创建且初始化2个地址结构client_addr --> bind()server_addr --> connect()

2.本地套 和 网络套对比

							网络套接字						                    									本地套接字server:lfd = socket(AF_INET, SOCK_STREAM, 0);												lfd = socket(AF_UNIX, SOCK_STREAM, 0);bzero() ---- struct sockaddr_in serv_addr;											bzero() ---- struct sockaddr_un serv_addr, clie_addr;serv_addr.sin_family = AF_INET;														serv_addr.sun_family = AF_UNIX;						serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);										strcpy(serv_addr.sun_path, "套接字文件名")serv_addr.sin_port = htons(8888);													len = offsetof(sockaddr_un, sun_path) + strlen();unlink("套接字文件名");								bind(lfd, (struct sockaddr *)&serv_addr, sizeof());									bind(lfd, (struct sockaddr *)&serv_addr, len);  创建新文件Listen(lfd, 128);																	Listen(lfd, 128);cfd = Accept(lfd, ()&clie_addr, &len);												cfd = Accept(lfd, ()&clie_addr, &len);  client:		lfd = socket(AF_INET, SOCK_STREAM, 0);												lfd = socket(AF_UNIX, SOCK_STREAM, 0);"隐式绑定 IP+port"																	bzero() ---- struct sockaddr_un clie_addr;clie_addr.sun_family = AF_UNIX;strcpy(clie_addr.sun_path, "client套接字文件名")len = offsetof(sockaddr_un, sun_path) + strlen();unlink( "client套接字文件名");bind(lfd, (struct sockaddr *)&clie_addr, len);bzero() ---- struct sockaddr_in serv_addr;											bzero() ---- struct sockaddr_un serv_addr;serv_addr.sin_family = AF_INET;														serv_addr.sun_family = AF_UNIX;inet_pton(AF_INT, "服务器IP", &sin_addr.s_addr)										strcpy(serv_addr.sun_path, "server套接字文件名")	serv_addr.sin_port = htons("服务器端口");											len = offsetof(sockaddr_un, sun_path) + strlen();				connect(lfd, &serv_addr, sizeof());													connect(lfd, &serv_addr, len);

server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>#include "wrap.h"#define SERV_ADDR  "serv.socket"int main(void)
{int lfd, cfd, len, size, i;struct sockaddr_un servaddr, cliaddr;char buf[4096];lfd = Socket(AF_UNIX, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sun_family = AF_UNIX;strcpy(servaddr.sun_path, SERV_ADDR);len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);     /* servaddr total len */unlink(SERV_ADDR);                                                            /* 确保bind之前serv.sock文件不存在,bind会创建该文件 */Bind(lfd, (struct sockaddr *)&servaddr, len);                                 /* 参3不能是sizeof(servaddr) */Listen(lfd, 20);printf("Accept ...\n");while (1) {len = sizeof(cliaddr);  //AF_UNIX大小+108Bcfd = Accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);len -= offsetof(struct sockaddr_un, sun_path);                           /* 得到文件名的长度 */cliaddr.sun_path[len] = '\0';                                            /* 确保打印时,没有乱码出现 */printf("client bind filename %s\n", cliaddr.sun_path);while ((size = read(cfd, buf, sizeof(buf))) > 0) {for (i = 0; i < size; i++)buf[i] = toupper(buf[i]);write(cfd, buf, size);}close(cfd);}close(lfd);return 0;
}

client.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>#include "wrap.h"#define SERV_ADDR "serv.socket"
#define CLIE_ADDR "clie.socket"int main(void)
{int  cfd, len;struct sockaddr_un servaddr, cliaddr;char buf[4096];cfd = Socket(AF_UNIX, SOCK_STREAM, 0);bzero(&cliaddr, sizeof(cliaddr));cliaddr.sun_family = AF_UNIX;strcpy(cliaddr.sun_path,CLIE_ADDR);len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path);     /* 计算客户端地址结构有效长度 */unlink(CLIE_ADDR);Bind(cfd, (struct sockaddr *)&cliaddr, len);                                 /* 客户端也需要bind, 不能依赖自动绑定*/bzero(&servaddr, sizeof(servaddr));                                          /* 构造server 地址 */servaddr.sun_family = AF_UNIX;strcpy(servaddr.sun_path, SERV_ADDR);len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);   /* 计算服务器端地址结构有效长度 */Connect(cfd, (struct sockaddr *)&servaddr, len);while (fgets(buf, sizeof(buf), stdin) != NULL) {write(cfd, buf, strlen(buf));len = read(cfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);}close(cfd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/103749.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】虚函数

2023年8月23日&#xff0c;周三上午 目录 虚函数在派生类中重写虚函数纯虚函数 示例程序 虚函数 在函数返回值前面加上关键字virtual虚函数必须在类中声明&#xff0c;否则会报错“[Error] virtual outside class declaration” class Base { public:virtual void func(); /…

Linux——初始

linux是一个操作系统&#xff0c;目前主流就是在服务器后端被选作服务器的操作系统来使用&#xff0c;所以我们没有直接接触到。 Linux的历史和概念 先是国家投钱给科研技术人员&#xff0c;科研技术人员将科研成果部分投入生活用品卖给老百姓&#xff0c;老百姓购买产品同时还…

使用Termux在安卓手机上搭建Hexo博客网站,并发布到公网访问

文章目录 1. 安装 Hexo2. 安装cpolar内网穿透3. 公网远程访问4. 固定公网地址 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并…

SpringBoot利用ConstraintValidator实现自定义注解校验

一、前言 ConstraintValidator是Java Bean Validation&#xff08;JSR-303&#xff09;规范中的一个接口&#xff0c;用于实现自定义校验注解的校验逻辑。ConstraintValidator定义了两个泛型参数&#xff0c;分别是注解类型和被校验的值类型。在实现ConstraintValidator接口时&…

RabbitMQ介绍

RabbitMQ的概念 RabbitMQ 是一个消息中间件&#xff1a;它接受并转发消息。你可以把它当做一个快递站点&#xff0c;当你要发送一个包裹时&#xff0c;你把你的包裹放到快递站&#xff0c;快递员最终会把你的快递送到收件人那里&#xff0c;按照这种逻辑 RabbitMQ 是 一个快递…

SpringBoot - 两种方式刷新配置信息

一、第一种方式 ​ConfigurationProperties​不能自动刷新&#xff0c;需要手动调用contextRefresher.refresh()方法来刷新配置。 import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;Component…

geacon_pro配合catcs4.5上线Mac、Linux

我的个人博客: xzajyjs.cn 一些链接 Try师傅的catcs4.5项目: https://github.com/TryGOTry/CobaltStrike_Cat_4.5&#xff0c;最新版解压密码见&#xff1a;https://www.nctry.com/2708.html geacon_pro: https://github.com/testxxxzzz/geacon_pro BeaconTool.jar: https:/…

qt显示图片并转换成灰度图及伪彩图

写了个程序&#xff0c;可在途图片&#xff0c;并切换成灰度图及伪彩图显示&#xff0c;主要代码如下&#xff1a; #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainW…

mysql 8.0 窗口函数 之 分布函数 与 sql server (2017以后支持) 分布函数 一样

mysql 分布函数 percent_rank&#xff08;&#xff09; &#xff1a;等级值 百分比cume_dist() &#xff1a;累积分布值 percent_rank&#xff08;&#xff09; 计算方式 (rank-1)/(rows-1)&#xff0c; 其中 rank 的值为使用RANK()函数产生的序号&#xff0c;rows 的值为当前…

TDesign中后台管理系统-用户登录

目录 1 创建用户表2 开发后端接口3 测试接口4 修改登录页面调用后端接口最终效果总结 中后台系统第一个要实现的功能就是登录了&#xff0c;我们通常的逻辑是让用户在登录页面输入用户名和密码&#xff0c;调用后端接口去验证用户的合法性&#xff0c;然后根据接口返回的结果进…

BERT、ERNIE、Grover、XLNet、GPT、MASS、UniLM、ELECTRA、RoBERTa、T5、C4

BERT、ERNIE、Grover、XLNet、GPT、MASS、UniLM、ELECTRA、RoBERTa、T5、C4 ELMOBERTERNIE![在这里插入图片描述](https://img-blog.csdnimg.cn/274e31d0f8274c748d05abe2ec65fc73.png)GroverXLNetGPTMASSUniLMELECTRARoBERTaT5C4ELMO BERT

小程序定位到 胶囊的三个点大概中间

话不多说&#xff0c;先上效果图 这个功能实现思路: 首先先拿到这一张整图(快捷&#xff0c;精确)然后获取整个导航栏高度(自定义导航栏,非自定义导航栏忽略这一步)获取三个点的做偏移量&#xff0c;把高度和偏移量给到一个定位到盒子&#xff0c;这个盒子里就放这个图片&…

蓝蓝设计-UI设计公司案例-HMI列车监控系统界面设计解决方案

2013年&#xff0c;为加拿大庞巴迪(Bombardier)设计列车监控系统界面设计。 2015-至今&#xff0c;为中车集团旗下若干公司提供HMI列车监控系统界面设计,综合考虑中车特点、城轨车、动车组的不同需求以及HMI硬键屏和触摸 屏的不同操作方式&#xff0c;重构框架设计、交互设计、…

Java【HTTP】什么是 Cookie 和 Session? 如何理解这两种机制的区别和作用?

文章目录 前言一、Cookie1, 什么是 Cookie2, Cookie 从哪里来3, Cookie 到哪里去4, Cookie 有什么用 二、Session1, 什么是 Session2, 理解 Session 三、Cookie 和 Session 的区别总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; …

Hadoop运行模式

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 文章目录 Hadoop运行模式本地运行模式&#xff08;官方WordCount&am…

三次握手四次挥手

三次握手和四次挥手是什么 TCP 是面向连接的协议&#xff0c;所以使用 TCP 前必须先建立连接&#xff0c;而建立连接是通过三次握手来进行的&#xff0c;断开连接是通过四次挥手来进行的。 建立连接&#xff1a;三次握手 关于下方用到的SYN ACK标志位&#xff0c;请点击此处…

记一次项目内存优化--内存泄漏

需求–内存泄漏优化&#xff0c;PSS有所下降&#xff0c; OOM率减少 主要是与某个版本作基准进行对比&#xff08;一般是最新版本的前一个版本作原数据&#xff09;&#xff0c;优化后&#xff0c;PSS有所下降&#xff0c;线上OOM率减少&#xff08;Bugly版本对比&#xff09;…

ERP系统解析:全面了解企业资源规划系统

在当今快节奏的商业环境中&#xff0c;有效的企业资源计划&#xff08;Enterprise Resource Planning&#xff0c;简称ERP&#xff09;系统对于组织的成功运营至关重要。ERP系统是一种集成管理软件&#xff0c;通过整合各个部门的信息和流程&#xff0c;实现资源的高效利用和运…

死锁的典型情况、产生的必要条件和解决方案

前言 死锁&#xff1a;多个线程同时被阻塞&#xff0c;他们中的一个或全部都在等待某个资源被释放。由于线程被无限期地阻塞&#xff0c;因此程序不可能正常终止。 目录 前言 一、死锁的三种典型情况 &#xff08;一&#xff09;一个线程一把锁 &#xff08;二&#xff09;…

单片机 (一) 让LED灯 亮

一&#xff1a;硬件电路图 二&#xff1a;软件代码 #include "reg52.h"#define LED_PORT P2void main() {LED_PORT 0x01; // 0000 0001 D1 是灭的 } #include "reg52.h" 这个头文件的作用&#xff1a;包含52 系列单片机内部所有的功能寄存器 三&#…