IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> linux-线程池、UDP通信与本地socket通信 -> 正文阅读

[网络协议]linux-线程池、UDP通信与本地socket通信

1 学习目标

  • 了解线程池模型的设计思想
  • 能看懂线程池实现源码
  • 掌握tcp和udp的优缺点和使用场景
  • 说出udp服务器通信流程
  • 说出udp客户端通信流程
  • 独立实现udp服务器代码
  • 独立实现udp客户端代码
  • 熟练掌握本地套接字进行本地进程通信

2 线程池

什么是线程池?
是一个抽象的概念,若干个线程组合到一起, 形成线程池。

为什么需要线程池?
多线程版服务器一个客户端就需要创建一个线程! 若客户端太多, 显然不太合适。

什么时候需要创建线程池呢?
简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。实现的时候类似于生产者和消费者。

线程池和任务池:
任务池相当于共享资源, 所以需要使用互斥锁, 当任务池中没有任务的时候需要让线程阻塞, 所以需要使用条件变量.

如何让线程执行不同的任务?
使用回到函数, 在任务中设置任务执行函数, 这样可以起到不同的任务执行不同的函数.

2.1 通过阅读线程池代码思考如下问题?

  • 熟悉结构体 threadpool_t
  • 线程池如何创建起来?
    • 各种初始化,malloc,pthread_create,pthread_cond_init pthread_mutex_init
  • 线程池内都有几类线程?
    • 2类:管理线程+工作线程
  • 管理者线程的任务是什么?任务如何实现?
    • 任务是添加线程或者删除线程,通过2个算法,删除线程 wait_exit_thr_num = 10
  • 工作线程如何工作?
    • 等待有任务,抢到任务,修改busy_thr_num ++ 执行任务 修改 busy_thr_num –
  • 线程池是如何销毁的?
    • 自爆shutdown 诱杀!

2.2 讲解代码threadpoolsimple.c

2.3 讲解代码 pthreadpool.c

3 UDP通信

TCP:传输控制协议, 面向连接的,稳定的,可靠的,安全的数据流传递

  • 稳定和可靠: 丢包重传
  • 数据有序: 序号和确认序号
  • 流量控制: 滑动窗口

UDP:用户数据报协议

  • 面向无连接的,不稳定,不可靠,不安全的数据报传递—更像是收发短信
  • UDP传输不需要建立连接,传输效率更高,在稳定的局域网内环境相对可靠

3.1 UDP通信相关函数介绍

3.1.1 接收消息 recvfrom

函数原型: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 原地址 传出参数
  • addrlen 发送方地址长度

返回值

  • 成功: 返回读到的字节数
  • 失败: 返回 -1 设置errno

调用该函数相当于TCP通信的recv+accept函数

3.1.2 发送数据

函数原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
函数说明: 发送数据
参数说明:

  • sockfd 套接字
  • dest_addr 目的地址
  • addrlen 目的地址长度

返回值

  • 成功: 返回写入的字节数
  • 失败: 返回-1,设置errno

在这里插入图片描述

3.2 UDP的服务器编码流程

  1. 创建套接字 type=SOCK_DGRAM
  2. 绑定ip和端口
  3. while(1)
    {
    收发消–recvfrom
    发消息–sendto
    }
  4. 关闭套接字–close

3.3 UDP客户端流程

  1. 创建套接字–socket
  2. while(1)
    {
    收发消–recvfrom
    发消息–sendto
    }
  3. 关闭套接字–close

3.4 编写udp代码并进行测试

测试: 多开器几个客户端经过测试表明:, udp天然支持多客户端, 这点和TCP不同, TCP需要维护连接.

使用nc命令进行测试: nc -u 127.1 8888

4 本地socket通信

回顾一些linux系统有哪些文件类型?
回顾一些linux系统下有哪些常见的IPC机制?

4.1 函数说明: 创建本地域:socket

通过查询: man 7 unix 可以查到unix本地域socket通信相关信息:

注意本地socket需要加头文件<sys/un.h>

#include <sys/socket.h>
#include <sys/un.h>
int socket(int domain, int type, int protocol);

函数参数:

  • domain: AF_UNIX or AF_LOCAL
  • t- ype: SOCK_STREAM或者SOCK_DGRAM
  • protocol: 0 表示使用默认协议

函数返回值:

  • 成功: 返回文件描述符.
  • 失败: 返回-1, 并设置errno值.

创建socket成功以后, 会在内核创建缓冲区, 下图是客户端和服务端内核缓冲区示意图.
在这里插入图片描述

4.2 绑定套接字:bind

函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数说明: 绑定套接字
函数参数:

  • socket: 由socket函数返回的文件描述符
  • addr: 本地地址
  • addlen: 本地地址长度

函数返回值:

  • 成功: 返回文件描述符.
  • 失败: 返回-1, 并设置errno值.

需要注意的是: bind函数会自动创建socket文件类型, 若在调用bind函数之前socket文件已经存在, 则调用bind会报错, 可以使用unlink函数在bind之前先删除文件。

struct sockaddr_un {
    sa_family_t sun_family;  /* AF_UNIX or AF_LOCAL*/
    char sun_path[108];  /* pathname */
};

在这里插入图片描述

通过man 2 bind, 可以查看bind函数的相关信息, 后面还有示例代码, 可以参考.

4.3 本地套接字服务器的流程

  • 可以使用TCP的方式, 必须按照tcp的流程
  • 也可以使用UDP的方式, 必须按照udp的流程

4.4 tcp的本地套接字服务器流程

  • 创建套接字 socket(AF_UNIX,SOCK_STREAM,0)
  • 绑定 struct sockaddr_un &强转
  • 侦听 listen
  • 获得新连接 accept
  • 循环通信 read-write
  • 关闭文件描述符 close
//本地socket通信服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>

int main()
{
	//创建socket
	int lfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(lfd<0)
	{
		perror("socket error");
		return -1;
	}

	//删除socket文件,避免bind失败
	unlink("./server.sock");

	//绑定bind
	struct sockaddr_un serv;
	bzero(&serv, sizeof(serv));
	serv.sun_family = AF_UNIX;
	strcpy(serv.sun_path, "./server.sock"); 
	int ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
	if(ret<0)
	{
		perror("bind error");
		return -1;
	}

	//监听listen
	listen(lfd, 10);

	//接收新的连接-accept
	struct sockaddr_un client;
	bzero(&client, sizeof(client));
	int len = sizeof(client);
	int cfd = accept(lfd, (struct sockaddr *)&client, &len);
	if(cfd<0)
	{
		perror("accept error");	
		return -1;
	}
	printf("client->[%s]\n", client.sun_path);

	int n;
	char buf[1024];

	while(1)
	{
		//读数据
		memset(buf, 0x00, sizeof(buf));		
		n = read(cfd, buf, sizeof(buf));
		if(n<=0)
		{
			printf("read error or client close, n==[%d]\n", n);
			break;
		}
		printf("n==[%d], buf==[%s]\n", n, buf);

		//发送数据
		write(cfd, buf, n);
	}

	close(lfd);

	return 0;
}


4.5 tcp本地套接字客户端流程:

  • 调用socket创建套接字
  • 调用bind函数将socket文件描述和socket文件进行绑定.不是必须的, 若无显示绑定会进行隐式绑定,但服务器不知道谁连接了.
  • 调用connect函数连接服务端
  • 循环通信read-write
  • 关闭文件描述符 close
//本地socket通信客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>


int main()
{
	//创建socket
	int cfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(cfd<0)
	{
		perror("socket error");
		return -1;
	}

	//删除socket文件,避免bind失败
	unlink("./client.sock");

	//绑定bind
	struct sockaddr_un client;
	bzero(&client, sizeof(client));
	client.sun_family = AF_UNIX;
	strcpy(client.sun_path, "./client.sock"); 
	int ret = bind(cfd, (struct sockaddr *)&client, sizeof(client));
	if(ret<0)
	{
		perror("bind error");
		return -1;
	}

	struct sockaddr_un serv;
	bzero(&serv, sizeof(serv));
	serv.sun_family = AF_UNIX;
	strcpy(serv.sun_path, "./server.sock");
	ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));
	if(ret<0)
	{
		perror("connect error");	
		return -1;
	}

	int n;
	char buf[1024];

	while(1)
	{
		memset(buf, 0x00, sizeof(buf));
		n = read(STDIN_FILENO, buf, sizeof(buf));

		//发送数据
		write(cfd, buf, n);

		//读数据
		memset(buf, 0x00, sizeof(buf));		
		n = read(cfd, buf, sizeof(buf));
		if(n<=0)
		{
			printf("read error or client close, n==[%d]\n", n);
			break;
		}
		printf("n==[%d], buf==[%s]\n", n, buf);
	}

	close(cfd);

	return 0;
}


4.6 编写代码并进行测试

测试客户端工具:
	man  nc 
		-U      Specifies to use UNIX-domain sockets.

例如: nc -U sock.s

5 offsetof函数

size = offsetof(struct sockaddr_un, sun_path) +strlen(un.sun_path);
#define offsetof(type, member) ((int)&((type *)0)->member)
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-01 14:49:27  更:2021-08-01 14:51:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 23:22:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码