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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 2021-11-07 -> 正文阅读

[网络协议]2021-11-07

网络编程第三天

一.网络编程四种IO模型

1.阻塞IO(同步IO)

read(),recv(),recvfrom()
这些函数本身是不阻塞的

(1)是open(),socket(),accept()
这些函数导致阻塞的,系统会默认将open(),socket(),accept()设置为阻塞

(2)UDP协议中套接字写sendto…操作是不会发生阻塞的

2.非阻塞IO

1.给文件描述符设置非阻塞属性 —>缺点 : 占用cpu资源
2.由于是非阻塞属性,所以需要用户不断的轮询,检测数据是否到达

3.多路复用

1.同时对多个IO进行操作
2.可以设置超时属性

4.信号驱动(异步IO)

1.是属于异步IO,不需要等待
2.当客户端接入,会触发对应信号

//-------------------------------

二.阻塞IO与非阻塞IO的差异

1.阻塞IO
建立套接字(阻塞,系统默认)—>读取数据—》判断缓冲区有没有数据—》没有数据—》阻塞进入休眠状态
有数据—》读取数据

2.非阻塞IO
建立套接字(阻塞,系统默认)—>设置非阻塞属性套接字—》读取数据—》判断缓冲区有没有数据–》没有数据–》读取失败–》接口返回
有数据----》读取数据

3.设置非阻塞属性的套接字
man 2 fcntl
#include <unistd.h>
#include <fcntl.h>

	int fcntl(int fd, int cmd, ... /* arg */ );
	参数:
		fd : 需要设置属性的文件描述符
		cmd : 指定选择文件描述符的的设置方式
			F_GETFL (void) : //获取文件描述符状态 成功返回文件的状态,失败返回 -1
			
			F_SETFL (int) : // 设置文件描述符状态,此状态需要第三个参数arg
			
		arg : 是否接收此参数是由cmd参数决定
			O_APPEND, :---》文件追加属性
			O_ASYNC, : ---》信号触发模式
			O_DIRECT, :---》不使用缓冲区写入 
			O_NOATIME ---》不更新文件的修改时间
			O_NONBLOCK --->非阻塞属性

例子 :
int fd = socket()
// 获取文件属性
int state = fcntl(fd,F_GETFL);
// 将非阻塞属性更新到文件状态
state |= O_NONBLOCK; //int state1 = state | O_NONBLOCK;
// 将新的状态重新设置回文件描述符
fcntl(fd,F_SETFL,state);

		fcntl()返回值是根据文件描述符的设置方式来决定的
			F_GETFL (void) : //获取文件描述符状态 成功返回文件的状态,失败返回 -1
			F_SETFL (int) : // 设置文件描述符状态,此状态需要第三个参数arg
		失败 -1

练习:
编写一个服务器需要监听等待客户端连接,如果有客户端添加进行,就将已连接的套接字放到数组
我们只能最多接收20个客户端,然后接收所有客户端发送过来的消息并打印出来

思路 :
1.将socket()设置为非阻塞,会导致accept()不发送阻塞,可以源源不断的接收客户端的connfd,将这个connfd
放到数组,并记录连接的个数

2.将accept()的返回值也设置为非阻塞,recv会导致非阻塞
struct client
{
int connfdbuf[20];
int count;
}

三、阻塞和非阻塞IO

1.2 服务端代码

Code

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

// 管理连接的客户端和连接人数的结构体
typedef struct client
{
	int client_fd[20]; // 存放已连接套接字
	int count;// 已连接的套接字个数,不能大于20
	
}client;

void init_client(client *mc_client)
{
	for(int i =0; i < 20;i++)
	{
		(mc_client->client_fd)[i] = -1; //20个连接值初始化为0
	}
	mc_client->count = 0; //代表还没有连接
}
//----------------------------
int init_tcp(int port)
{
	// 创建套接字,TCP  sockfd默认是阻塞的
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("open socket failed ");
		return -1;
	}
	
	// 获取文件属性socket()设置为非阻塞以后,accept()是不会等待连接
	 int state = fcntl(sockfd,F_GETFL);
	// 将非阻塞属性更新到文件状态
	 state |=  O_NONBLOCK;  //int state1 = state | O_NONBLOCK;
	// 将新的状态重新设置回文件描述符
	fcntl(sockfd,F_SETFL,state);   //sockfd非阻塞了
	
	// 设置服务器ip和端口号
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	// 设置自动获取ip
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	socklen_t len = sizeof(saddr);
	
	//绑定套接字
	int ret = bind(sockfd, (struct sockaddr *)&saddr, len);
	if(ret == -1)
	{
		perror("socket bind failed ");
		return -1;
	}
	
	// 监听套接字
	listen(sockfd,20);	 //设置监听
	
	return sockfd;
}

//----------------------------
bool add_client(client *mc_client,int connfd)
{
	// 判断结构体是否满了
	if(mc_client->count > 20)
		return false;

	// 添加,将连接的返回值传入数组
	mc_client->client_fd[mc_client->count++] = connfd;
	return true;
}
//----------------------------
int main(int argc,char **argv)
{
	if(argc != 2)
	{
		printf("传递端口号\n");
		return -1;
	}
	
	// 给结构体分配空间
	client * mc_cilent = malloc(sizeof(client));
	if(mc_cilent == NULL)
	{
		perror("malloc failed : ");
		return -1;
	}
	
	// 初始化已分配的结构体空间
	init_client(mc_cilent);
	
	// 初始化tcp
	int port = atoi(argv[1]);
	int sock_fd = init_tcp(port);
	if(sock_fd == -1)
	{
		return -1;
	}
	
	// 定义客户端地址结构体,用与获取客户端的ip和端口号
	struct sockaddr_in clientaddr;
	socklen_t len = sizeof(clientaddr);
	bzero(&clientaddr,len);
	char *client_ip = NULL;
	int connfd;
	while(1)
	{
		// 不断轮询有没有客户端进来
		connfd = accept(sock_fd, (struct sockaddr *)&clientaddr, &len);
		if(connfd > 0)
		{
			printf("连接成功\n");
			
			// 将已连接套接字设置为非阻塞属性,recv(),read()就不会发生阻塞
			// 获取文件属性
			int state = fcntl(connfd,F_GETFL);
			// 将非阻塞属性更新到文件状态
			 state |=  O_NONBLOCK;  //int state1 = state | O_NONBLOCK;
			// 将新的状态重新设置回文件描述符
			fcntl(connfd,F_SETFL,state);
			
			// 将新连接的客户端文件描述符添加到结构体
			add_client(mc_cilent,connfd);
			
			// 获取客户端ip
			client_ip = inet_ntoa(clientaddr.sin_addr);
		}
	
		// 轮询打印客户端的信息
		for(int i =0; i < 20; i++)
		{
			char buf[100] = {0};
			// 如果没有客户退出
			if(mc_cilent->client_fd[i] == -1)
			{
				break;
			}
			int n;
			// 接收客户端信息
			if(n = read((mc_cilent->client_fd[i]),buf,100) > 0)
			{
				printf("from %s : %s\n",client_ip,buf);
			}
		}
		
	}
	free(mc_cilent);
	close(sock_fd);
	close(connfd);
	return 0;
}


1.2客户端

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>


int main(int argc,char **argv)
{
	if(argc != 3)
	{
		printf("传递ip和端口号\n");
		return -1;
	}
	
	// 创建套接字,TCP
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("open socket failed ");
		return -1;
	}
	
	// 设置服务器ip和端口号
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(atoi(argv[2]));
	// 设置自动获取ip
	saddr.sin_addr.s_addr = inet_addr(argv[1]);
	
	socklen_t len = sizeof(saddr);
	
	// 连接服务器
	int ret = connect(sockfd, (struct sockaddr *)&saddr, len);
	if(ret == -1)
	{
		perror("connect failed : ");
		return -1;
	}
	
	while(1)
	{
		char buf[100] = {0};
		fgets(buf,100,stdin);
		send(sockfd,buf,sizeof(buf),0);
	} 
	
	//设置非阻塞属性服务器(将accept()设置为非阻塞),客户端连接发送数据,查看效果
	
	return 0;
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-09 19:59:31  更:2021-11-09 20:01:23 
 
开发: 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年7日历 -2024/7/1 21:15:21-

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