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.mzph.cn/news/51413.shtml

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

相关文章

electron软件安装时,默认选择为全部用户安装

后续可能会用electron开发一些工具&#xff0c;包括不限于快速生成个人小程序、开发辅助学习的交互式软件、帮助运维同学一键部署的简易版CICD工具等等。 开发进度&#xff0c;取决于我懒惰的程度。 不过不嫌弃的同学还是可以先关注一波小程序&#xff0c;真的发布工具了&…

微软宣布在 Excel 中使用 Python:结合了 Python 的强大功能和 Excel 的灵活性。

自诞生以来&#xff0c;Microsoft Excel 改变了人们组织、分析和可视化数据的方式&#xff0c;为每天使用它的数百万人提供了决策基础。今天&#xff0c;我们宣布发布 Excel 中的 Python 公共预览版&#xff0c;从而使 Excel 中的分析功能取得重大进展。 Excel 中的 Python 可…

基于springboot+vue的考研资讯平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【洛谷】P1873 [COCI2011-2012#5] EKO / 砍树

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1873 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二分答案。 3. 代码实现 #include<bits/stdc.h> using namespace std; #define ll long long const int N 1e6 10; int a[N], …

系统学习Linux-LVS集群

集群概述 负载均衡技术类型 四层负载均衡器 也称为 4 层交换机&#xff0c;主要通过分析 IP 层及 TCP/UDP 层的流量实现基于 IP 加端口的负载均衡&#xff0c;如常见的 LVS、F5 等&#xff1b; 七层负载均衡器 也称为 7 层交换机&#xff0c;位于 OSI 的最高层&#xff0c;即…

数据库表结构导出为word、html、markdown【转载,已解决,已验证,开源】

注&#xff1a;本文为gitcode代码验证&#xff0c;转载gitcode gitcode&#xff1a;https://gitcode.net/mirrors/pingfangushi/screw?utm_sourcecsdn_github_accelerator 整理数据库文档&#xff1a;https://mp.weixin.qq.com/s/Bo_U5_cl82hfQ6GmRs2vtA <!--数据库文档核…

Seaborn数据可视化(三)

1.绘制直方图 使用displot()绘制直方图。 import seaborn as sns import numpy as np import matplotlib.pyplot as plt# 生成随机数据 np.random.seed(0) data np.random.randn(1000)# 使用displot绘制直方图 sns.displot(data, bins10, kdeTrue)# 展示图形 plt.show() 结…

Python用 tslearn 进行时间序列聚类可视化

全文链接&#xff1a;https://tecdat.cn/?p33484 我们最近在完成一些时间序列聚类任务&#xff0c;偶然发现了 tslearn 库。我很想看看启动和运行 tslearn 已内置的聚类有多简单&#xff0c;结果发现非常简单直接&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09…

Nexus私有仓库+IDEA配置远程推送

目录 一、docker安装nexus本地私服&#xff0c;Idea通过maven配置deploy本地jar包&#xff08;简单&#xff09; 二、docker push镜像到第三方nexus远程私服&#xff08;shell命令操作&#xff09; 三、springboot通过maven插件自动生成docker镜像并push到nexus私服&#xf…

【科研】-- 如何将Endnote中参考文献格式插入到Word?

文章目录 如何将Endnote中参考文献格式插入到Word&#xff1f; 如何将Endnote中参考文献格式插入到Word&#xff1f; 1、首先确保Endnote和Word安装正确&#xff0c;正常可以从学校官网中下载到正版软件&#xff0c;下载后在word栏目中会出现EndNote的标签&#xff1b; 2、可…

[Linux]进程状态

[Linux]进程状态 文章目录 [Linux]进程状态进程状态的概念阻塞状态挂起状态Linux下的进程状态孤儿进程 进程状态的概念 了解进程状态前&#xff0c;首先要知道一个正在运行的进程不是无时无刻都在CPU上进行运算的&#xff0c;而是在操作系统的管理下&#xff0c;和其他正在运行…

音视频FAQ(二)视频直播延时高

摘要 延时高是实时互动技术中常见的问题之一&#xff0c;解决延时高问题需要综合考虑网络、设备、编解码算法等多个因素。解决方案包括优化设备端延时、优化网络传输延时和使用UDP进行音视频传输等。在选择音视频传输协议时&#xff0c;需要综合考虑实际需求和网络条件&#x…

Linux —— keepalived

简介 Keepalived 是一个用 C 语言编写的路由软件。这个项目的主要目标是为 Linux 系统和基于 Linux 的基础设施提供简单而强大的负载均衡和高可用性功能。 Keepalived 开源并且免费的软件。 Keepalived 的2大核心功能 1. loadbalance 负载均衡 LB&#xff1a;ipvs--》lvs软件…

线性代数的学习和整理12: 矩阵与行列式,计算上的差别对比

目录 1 行列式和矩阵的比较 2 简单总结矩阵与行列式的不同 3 加减乘除的不同 3.1 加法不同 3.2 减法不同 3.3 标量乘法/数乘 3.3.1 标准的数乘对比 3.3.2 其他数乘对比 3.4 乘法 4 初等线性变换的不同 4.1 对矩阵进行线性变换 4.2 对行列式进行线性变换呢&#xf…

Maven详解

文章目录 一、引言1.1 为什么需要 Maven&#xff1f;1.2 Maven 解决了哪些问题&#xff1f;1.2.1 添加第三方jar包1.2.2 jar包之间的依赖关系1.2.3 处理jar包之间的冲突1.2.4 获取第三方jar包1.2.5 将项目拆分成多个工程模块1.2.6 实现项目的分布式部署 二、介绍三、Maven 的特…

flutter 雷达图

通过CustomPainter自定义雷达图 效果如下 主要代码 import package:flutter/material.dart; import dart:math; import dash_painter.dart; import model/charts_model.dart;class RadarChart extends StatelessWidget {final List<ChartModel> list;final double maxV…

5.物联网LWIP之UDP编程,stm32作为服务器实现大小写转化

UDP编程模型 1.UDP C/S模型&#xff08;代码流程只需要根据以下模型去输入即可&#xff09; 2.UDP API socket int socket(int domain, int type, int protocol); domain: AF_INET 这是大多数用来产生socket的协议&#xff0c;使用TCP或UDP来传输&#xff0c;用IPv4的地址…

从0开始配置eslint

没有在.eslintrc文件中配置parserOptions指定语言版本和模块类型 {"parserOptions": {"ecmaVersion": 7, //指定es版本为es2016"sourceType": "module", //使用import导入模块} }eslint还不能识别jsx语法 {"parserOptions"…

centos7物理机安装并配置外网访问

安装准备工作 安装之前需要准备一下&#xff0c;需要一个U盘&#xff0c;其次需要准备以下内容 1.需要centos7的ISO系统镜像 2.使用UltraISO软件写入ISO镜像 3.一台windows系统 将系统写入到U盘&#xff0c;写入步骤 打开UltraISO点击文件 → 打开&#xff0c;选择Linux镜…

告别数字化系统“物理叠加”,华为云推动智慧门店价值跃迁

文|智能相对论 作者|叶远风 有大屏幕滚动播放广告&#xff1b; 有人脸识别系统让消费者自助结账&#xff1b; 有订单管理系统综合分析一段时间内总体经营情况&#xff1b; 有全门店监控直连总部机房&#xff1b; …… 以搭载数字化系统的硬件设备为表面特征的智慧门店&a…