文章目录:
一:线程池模块分析
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 -------------------170 4. 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 0 typedef 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模型
文章来源:https://www.toymoban.com/news/detail-670220.html
UDP实现的 C/S 模型: recv()/send() 只能用于 TCP 通信。 替代 read、write accpet(); ---- 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: 0 src_addr:(struct sockaddr *)&addr 传出。 对端地址结构 addrlen:传入传出。 返回值: 成功接收数据字节数。 失败:-1 errn。 0: 对端关闭。 小-- 大 write();--- 被替换 --- sendto()---- connect ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); sockfd: 套接字 buf:存储数据的缓冲区 len:数据长度 flags: 0 src_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 8000 int 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 8000 int 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.本地套接字
文章来源地址https://www.toymoban.com/news/detail-670220.html
本地套接字: 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_un struct 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大小+108B cfd = 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客户端和服务器_本地和网络套接字的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!