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; }
相关文章:

Linux网络编程:线程池并发服务器 _UDP客户端和服务器_本地和网络套接字
文章目录: 一:线程池模块分析 threadpool.c 二:UDP通信 1.TCP通信和UDP通信各自的优缺点 2.UDP实现的C/S模型 server.c client.c 三:套接字 1.本地套接字 2.本地套 和 网络套对比 server.c client.c 一:线…...

nvm安装electron开发与编译环境
electron总是安装失败,下面说一下配置办法 下载软件 nvm npmmirror 镜像站 安装nvm 首先最好卸载node,不卸载的话,安装nvm会提示是否由其接管,保险起见还是卸载 下载win中的安装包 配置加速节点nvm node_mirror https://npmmi…...
玩转Mysql系列 - 第7篇:玩转select条件查询,避免采坑
这是Mysql系列第7篇。 环境:mysql5.7.25,cmd命令中进行演示。 电商中:我们想查看某个用户所有的订单,或者想查看某个用户在某个时间段内所有的订单,此时我们需要对订单表数据进行筛选,按照用户、时间进行…...
启动程序结束程序打开指定网页
import subprocess subprocess.Popen(r"C:\\Program Files\\5EClient\\5EClient.exe") # 打开指定程序 import os os.system(TASKKILL /F /IM notepad.exe) # 结束指定程序 import webbrowser webbrowser.open_new_tab(https://www.baidu.com) # 打开指定网页...
从零开始学习 Java:简单易懂的入门指南之包装类(十九)
包装类 包装类5.1 概述5.2 Integer类5.3 装箱与拆箱5.4 自动装箱与自动拆箱5.5 基本类型与字符串之间的转换基本类型转换为StringString转换成基本类型 5.6 底层原理 算法小题练习一:练习二:练习三:练习四:练习五: 包装…...
leetcode分类刷题:哈希表(Hash Table)(一、数组交集问题)
1、当需要快速判断某元素是否出现在序列中时,就要用到哈希表了。 2、本文针对的总结题型为给定两个及多个数组,求解它们的交集。接下来,按照由浅入深层层递进的顺序总结以下几道题目。 3、以下题目需要共同注意的是:对于两个数组&…...

UML四大关系
文章目录 引言UML的定义和作用UML四大关系的重要性和应用场景关联关系继承关系聚合关系组合关系 UML四大关系的进一步讨论UML四大关系的实际应用软件开发中的应用其他领域的应用 总结 引言 在软件开发中,统一建模语言(Unified Modeling Language&#x…...
forms组件(钩子函数(局部钩子、全局钩子)、三种页面的渲染方式、数据校验的使用)、form组件的参数以及单选多选形式
一、form是组件 后端代码 from django.shortcuts import render, redirect, HttpResponsedef ab_form(request):back_dict {username: , password: }if request.method POST:username request.POST.get(username)password request.POST.get(password)if 金瓶梅 in userna…...

跨专业申请成功|金融公司经理赴美国密苏里大学访学交流
J经理所学专业与从事工作不符,尽管如此,我们还是为其成功申请到美国密苏里大学经济学专业的访问学者职位,全家顺利过签出国。 J经理背景: 申请类型: 自费访问学者 工作背景: 某金融公司经理 教育背景&am…...
第十一章 CUDA的NMS算子实战篇(下篇)
cuda教程目录 第一章 指针篇 第二章 CUDA原理篇 第三章 CUDA编译器环境配置篇 第四章 kernel函数基础篇 第五章 kernel索引(index)篇 第六章 kenel矩阵计算实战篇 第七章 kenel实战强化篇 第八章 CUDA内存应用与性能优化篇 第九章 CUDA原子(atomic)实战篇 第十章 CUDA流(strea…...
R语言01-数据类型
概念 数值型(Numeric):用于存储数值数据,包括整数和浮点数。例如:x <- 5。 字符型(Character):用于存储文本数据,以单引号或双引号括起来。例如:name &l…...

【网络基础实战之路】基于三层架构实现一个企业内网搭建的实战详解
系列文章传送门: 【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 【网络基础实战之路】基于…...
C++11相较于C++98多了哪些可调用对象?--《包装器》篇
C98里面的可调用对象只有普通函数和函数指针。 而在C11里面可调用的对象有下面几种: 普通函数函数指针仿函数lambda表达式(匿名函数)包装器 普通函数、函数指针、仿函数、lambda表达式我在以前的文章里其实已经介绍过了 包装器 在C11里面有…...
栈与队列:常见的线性数据结构
栈(Stack)和队列(Queue)是计算机科学中常见的线性数据结构,它们在许多算法和编程场景中发挥着重要作用。它们的不同特点和用途使得它们适用于不同的问题和应用。 栈(Stack) 栈,作为…...
android framework之AMS的启动管理与职责
AMS是什么? AMS管理着activity,Service, Provide, BroadcastReceiver android10后:出现ATMS,ActivityTaskManagerService:ATMS是从AMS中抽出来,单独管理着原来AMS中的Activity组件 。 现在我们对AMS的分析,也就包含对…...
Decoupling Knowledge from Memorization: Retrieval-augmented Prompt Learning
本文是LLM系列的文章,针对《Decoupling Knowledge from Memorization: Retrieval 知识与记忆的解耦:检索增强的提示学习 摘要1 引言2 提示学习的前言3 RETROPROMPT:检索增强的提示学习4 实验5 相关实验6 结论与未来工作 摘要 提示学习方法在…...

腾讯云coding平台平台inda目录遍历漏洞复现
前言 其实就是一个python的库可以遍历到,并不能遍历到别的路径下,后续可利用性不大,并且目前这个平台私有部署量不多,大多都是用腾讯云在线部署的。 CODING DevOps 是面向软件研发团队的一站式研发协作管理平台,提供…...
无法正常访问服务器
网络原因,本地网络:解决办法:检查本地网络是否正常,访问外网是否流畅。机房网络:通过路由追踪查看是否中间有 节点不通,确定是线路出现丢包。 远程连接,检查远程连接是否启用以及远程计算机上的…...

解决css英文内容不自动换行的问题
解决css英文内容不自动换行的问题 这里主要是针对CMS后台管理系统添加进入数据库,再抓取出来前端显示的英文不换行的问题的情况 1.一般常见的就是英文不自动换行,或者英文换行单词背截断的问题。 这种处理方法通过前端样式就可以解决,方法网…...
python语言学习
序言 此系列用于总结python语言的相关知识点,用于帮助自己和有缘人查阅 1、python基本数据类型 python基本数据类型 – 字符串...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...