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  -------------------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模型

 


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.本地套接字

本地套接字:

	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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ogg怎么转mp3格式?让我们一起来学习吧

ogg怎么转mp3格式&#xff1f;如今&#xff0c;有许多种音频格式可供选择&#xff0c;其中包括了很多小伙伴可能并不熟悉的OGG音频格式。OGG的全称是OGG Vorbis&#xff0c;它是一种免费开放且没有使用限制的音频格式&#xff0c;因此备受许多小伙伴的喜爱。然而&#xff0c;OG…

打破数据孤岛,实现文档数据互通

随着数字经济加速发展&#xff0c;企业数字化转型正向更深层次推进。非结构化数据量也正在飞速增长&#xff0c;这些数据以文档、图片、音频等形式散落在组织内部&#xff0c;这给数据的整理和统一利用增加了难度。由于部门、应用、框架、多云环境等原因形成非结构化数据孤岛。…

【React源码实现】元素渲染的实现原理

前言 本文将结合React的设计思想来实现元素的渲染&#xff0c;即通过JSX语法的方式是如何创建为真实dom渲染到页面上&#xff0c;本文基本不涉及React的源码&#xff0c;但与React的实现思路是一致的&#xff0c;所以非常适合小白学习&#xff0c;建议跟着步骤敲代码&#xff…

csp认证真题——重复局面——Java题解

目录 题目背景 问题描述 输入格式 输出格式 样例输入 样例输出 样例说明 子任务 提示 【思路解析】 【代码实现】 题目背景 国际象棋在对局时&#xff0c;同一局面连续或间断出现3次或3次以上&#xff0c;可由任意一方提出和棋。 问题描述 国际象棋每一个局面可以…

01JVM_内存结构

一、什么是JVM 1.JVM的定义 Java程序的运行环境&#xff0c;java二进制字节码的运行环境 2.JVM的好处 ①一次编写&#xff0c;到处运行 ②自动内存管理&#xff0c;垃圾回收功能 ③数组下标越界检查 ④多态 3.jvm&#xff0c;jre&#xff0c;jdk的比较 3.常见的JVM 主…

Mac下Docker Desktop安装命令行工具、开启本地远程访问

Mac系统下&#xff0c;为了方便在terminal和idea里使用docker&#xff0c;需要安装docker命令行工具&#xff0c;和开启Docker Desktop本地远程访问。 具体方法是在设置-高级下&#xff0c; 1.将勾选的User调整为System&#xff0c;这样不用手动配置PATH即可使用docker命令 …

说说我最近筛简历和面试的感受。。

大家好&#xff0c;我是鱼皮。 都说现在行情不好、找工作难&#xff0c;但招人又谈何容易&#xff1f;&#xff01; 最近我们公司在招开发&#xff0c;实习社招都有。我收到的简历很多&#xff0c;但认真投递的、符合要求的却寥寥无几&#xff0c;而且都是我自己看简历、选人…

对于uts namespace共享的测试

前言 单单以下列命令运行虽然是root&#xff0c;还不行&#xff0c;我们需要加--privileged&#xff0c;不然会报 hostname: you must be root to change the host name docker run -it --utshost ubuntu:latest /bin/bash 如果加上--privileged后 docker run -it --priv…

基于AI智能分析网关EasyCVR视频汇聚平台关于能源行业一体化监控平台可实施应用方案

随着数字经济时代的到来&#xff0c;实体经济和数字技术深度融合已成为经济发展的主流思路。传统能源行业在运营管理方面也迎来了新的考验和机遇。许多大型能源企业已开始抓住机遇&#xff0c;逐步将视频监控、云计算、大数据和人工智能技术广泛应用于生产、维护、运输、配送等…

hp惠普光影精灵5笔记本HP Pavilion Gaming-15-dk0135tx原装出厂Win10系统

原厂系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、惠普电脑管家等预装程序 适用型号&#xff1a; 15-dk0011tx,15-dk0018tx,15-dk0019tx,15-dk0020tx,15-dk0021tx,15-dk0038tx 15-dk0039tx,15-dk0040tx,15-dk0041tx,15-dk0125tx,15-dk0126tx,15-dk0127tx 15-dk012…

排序 Bubble Sort(提取函数)

C自学精简教程 目录(必读) 1 前驱知识点 3.5 for循环语句 3.6 if语句 3.7 函数 3.8 动态内存 2 排序 是将元素按照从小到大的顺序存放的方法。 一开始元素可能并不是按照从小到大的顺序存放的。 这时候我们需要找到需要调整的元素对&#xff0c;并交换这两个元素的值&…

Flutter实现动画列表AnimateListView

由于业务需要&#xff0c;在打开列表时&#xff0c;列表项需要一个从右边飞入的动画效果&#xff0c;故封装一个专门可以执行动画的列表组件&#xff0c;可以自定义自己的动画&#xff0c;内置有水平滑动&#xff0c;缩放等简单动画。花里胡哨的动画效果由你自己来定制吧。 功…

springboot实战(二)之将项目上传至远程仓库

目录 环境&#xff1a; 背景&#xff1a; 操作&#xff1a; 1.注册码云账号 2.创建仓库 步骤&#xff1a; 1.注册完码云账号后&#xff0c;点击加号&#xff0c;新建仓库 2.输入项目名称和介绍&#xff0c;点击创建 3.复制仓库地址&#xff0c;你可以选择https协议或者…

VSAN硬盘出现resetremoved

原创作者&#xff1a;运维工程师 谢晋 VSAN硬盘出现reset&removed 客户环境有8台服务器dell R740和R740XD服务器组成了一套VSAN集群&#xff0c;但R740那四台的物理机老是出现硬盘故障需进行硬盘更换&#xff0c;后发现刚换完的硬盘没过几天又坏了&#xff0c;先开始怀疑…

C++学习笔记总结练习:运算符重载两种方式

运算符重载的两种方式 1 基本概念 基础 运算符时具有特殊名字的函数&#xff1a;由关键字operator和气候定义的运算符共同组成。 可以被重载的运算符 方式 将运算符重载为类的成员函数。重载运算符函数&#xff0c;并声明为类的友元。 规则 重载后的运算符必须至少有一个…

vscode html使用less和快速获取标签less结构

扩展插件里面搜索 css tree 插件 下载 使用方法 选择你要生成的标签结构然后按CTRLshiftp 第一次需要在输入框输入 get 然后选择 Generate CSS tree less结构就出现在这个里面直接复制到自己的less文件里面就可以使用了 在html里面使用less 下载 Easy LESS 插件 自己创建…

深圳产品展示视频拍摄一站式服务

产品展示视频拍摄一站式服务是指一家专业的拍摄制作公司或团队提供从策划、拍摄到后期制作的全方位服务&#xff0c;以满足客户的产品展示需求。这种服务通常包括以下方面&#xff0c;由产品展示视频制作公司老友记小编从以下几个方面为您整理&#xff1a; 1.策划和预制阶段&a…

【期末复习笔记】计算机操作系统

计算机操作系统 进程的描述与控制程序执行进程进程的定义与特征相关概念定义特征进程与程序的区别 进程的基本状态和转换PCBPCB中的信息作用PCB的组织方式 线程进程与线程的比较 处理机调度与死锁处理机调度处理机调度的层次 调度算法处理机调度算法的目标处理机调度算法的共同…

PhpStorm安装篇

PhpStorm安装篇: 下载地址 : 进入官网下PhpStorm: PHP IDE and Code Editor from JetBrains 下载完之后&#xff0c;安装包 安装目录建议不要放C盘&#xff0c;放其他盘&#xff0c;其他直接一直点 next &#xff0c;到结束 安装完&#xff0c;打开编辑器 注册账号并登录后…

继续绷紧油市神经,市场预计沙特10月继续自愿减产

KlipC报道&#xff1a;据了解&#xff0c;市场参与者大都认为沙特阿拉伯将会把自愿额外减产的措施延长至10月底&#xff0c;以寻求在经济低迷的背景下提振油价。 KlipC的合伙人Andi D表示&#xff1a;“今年5月起&#xff0c;沙特就自愿减产日均50万桶原油&#xff0c;今年6月初…