一、IO模型
一、基本概念
(一)I/O基本概念
1、基本概念
1)一个完整I/O分为两个阶段:
用户进程空间->内核空间
内核空间->设备空间(磁盘、网卡)
2)内存I/O(无名管道)、网络I/O(UDP/TCP)、磁盘I/O(文件I/O)
2、同步和异步
1)不着急等待结果是异步aio_read
之前的程序read都是同步
2)同步经常用于线程内调用两个函数的调用
3)异步请求不知道的结果,后面可能通过别的机制来获得结果。
2、阻塞与非阻塞
3、阻塞状态不占有cpu,就绪态是等待cpu
4、线程阻塞的条件
1)自动sleep
2)i/o操作阻塞,等待返回
3)等待锁,获得锁
4)等待某个触发条件
5、阻塞套接字
服务端等待接收客户端,如果没有客户端,阻塞状态执行。
6、套接字:网络通信进程的一端
二、阻塞IO与非阻塞IO
1、五种IO
信号驱动式又名同步
2、全程阻塞,结束出来
套接字文件描述符默认是阻塞的。
recvfrom、read、accept、connect
3、非阻塞IO
一直返回,返回错误,知道有数据,返回成功
三、多路复用IO,信号驱动IO和异步IO
(一)多路复用模型
1、同时处理多个文件描述符
1)例:select可以同时监听多个文件描述符,都没有数据等待阻塞,有准备好的数据,recvfrom返回可读条件,多路复用的目的就是,哪个有数据,返回可读条件,系统调用
2)多路复用第一个阶段等待数据可阻塞可不阻塞,第二个阶段是以阻塞方式进行的。
3)阻塞肯定是同步的,异步不可能阻塞
2、信号驱动式IO
第一个阶段等待数据是异步的,只是注册了一个信号,没有发起读写,进程仍然可以执行其他。
第二个阶段是同步的
3、异步IO(了解)
第一阶段系统调用后直接返回,然后再内核中,准备数据,拷贝数据都是非阻塞的,拷贝完成重新发信号,此时没有IO操作了
四、五种IO模型的比较
二、IO多路复用select函数
一、IO多路复用select函数
1、IO多路复用模型
1)select:复用fd_set文件集合,可以把多个文件描述符集合,调用select函数,调用内核
2、select函数
1)nfds:文件描述符
2)readfds:可读
writefds:可写
exceptfds:错误流
本次定义关心什么
3)timeout:时间颗粒us
4)其他多路复用函数poll/epoll API
可读集合最常用,可写一般不阻塞,异常直接处理
5)timeout = 0非阻塞
NULL:永久阻塞
6)select不仅是多路复用,一般函数等待时间是ms,select函数等待颗粒度细,只设置最后一个参数,达到阻塞几us
3、fd_set结构体
1)一次申请8字节,64bit位,当nfds满了,再次申请8个字节,直到进程最大文件描述符(1024/2048字节)
2)在内核中,只观察置1,也就是3,4,6文件描述符有没有数据
二、多路复用select实现
三、select实现代码优化
三、多路复用poll函数
一、多路复用poll函数
1、多路复用
2、poll函数
第一个参数:复用的数组
第二个参数:顺序表的字段
第三个参数:ms
revents:系统返回
0:立刻返回,负数永久阻塞
二、poll的实现
四、套接字属性
一、选项级别
1、选项级别:套接字实现了tcp、ip还有一些链路和以太网的功能,不同选项级别对应不同的层次
2、获取选项
3、选项相当于属性,套接字选项也可以称为套接字属性
4、选项可以分为只可获取,不可设置,有些选项即可获取也可设置
5、选项级别
6、socket级别
监听有listen函数,所以只能获取不能监听
7、ip级别
8、获取套接字函数
二、getsockopt获取套接 字选项
三、setsockopt设置套接字选项
五、广播和组播
一、广播及实现
1、ip地址分为网络号和主机号,主机号全为1的是广播地址
2、广播
1)广播只能用udp实现,tcp不行
2)255.255.255.255在所有网段中都代表广播地址
3、广播的实现
1)在udp服务端加上setsockopt即可
2)广播地址Bcast
3)代码/net/broadcast/
4)结果
5)抓包
因为是广播,可以用windows抓包
如果是windows中经过路由器转发,可以抓到两次包
二、组播及实现
1、部分主机可以收到
1)D类地址是多播ip,一个ip就是一个多播组,只需要将主机加入,可以是不同网络下的主机
2、多播IP地址
1)多播数据报目的地址要写入多播组的标识
2)一个D类地址标志一个多播组
3)多播地址只能用于目的地址,不能用于源地址
4)源地址向多播组发送数据,在该组播内的主机都可以收到数据
3、接收方
4、组播的实现
六、链路层原始套接字(扩展)
一、链路层原始套接字
1、以太网地址族
二、网络层原始套接字
1、创建
IPv4/原始套接字/抓取的是tcp的包
2、抓包代码
/net/raw
七、域名解析与http服务器实现原理
一、域名解析gethostbyname函数
1、域名解析的含义
ping一个网络,返回一个ip地址,原理就是域名解析函数
2、结构体
3、实现
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int i;
if(argc < 2){
printf("%s <host name>\n", argv[0]);
exit(0);
}
struct hostent *host = gethostbyname(argv[1]);
for(i = 0; host->h_aliases[i] != NULL; i++){
printf("%s\n", host->h_aliases[i]);
}
printf("Address type:%s\n",
host->h_addrtype == AF_INET ? "AF_INET":"AF_INET6");
for(i = 0; host->h_addr_list[i] != NULL; i++){
printf("IP address %d:%s\n", i, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));
}
endhostent();
return 0;
}
二、万维网服务器的实现原理
1、万维网基于http协议
1)打开一个网页,客户端先发送,服务端再发给客户端的浏览器,解析成网页
2)HTTP基于tcp,buf换成http请求的内容,和响应的报文
2、HTTP本质上是一个字符串
因此用nc可以访问服务器,只不过客户端可以直接使用网站,nc命令需要获得ip地址和端口(makeru端口80),ping网站可以得到ip地址
3、html解释性语言
4、将万维网的语言复制
可以实现html版本的代码
html实际上传的是字符串,核心是标记语言的文本文件
5、理解万维网的实现思路
实现思路:
1、编写http.txt脚本
2、nc -l 0 80检测本地
3、浏览器访问本地
4、本地服务器接收到报文
5、回传http脚本语言
结果:
三、万维网服务器实现
1、万维网包头
2、html脚本
3、服务器
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <strings.h>
#define PORT 80
#define BACKLOG 5
#define HTTPFILE "http-head.txt"
#define HTMLFILE "home.html"
int ClientHandle(int newfd);
int main(int argc, char *argv[])
{
int fd, newfd;
struct sockaddr_in addr;
/*创建套接字*/
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0){
perror("socket");
exit(0);
}
int opt = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &opt, sizeof(opt) ))
perror("setsockopt");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = 0;
/*绑定通信结构体*/
if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
perror("bind");
exit(0);
}
/*设置套接字为监听模式*/
if(listen(fd, BACKLOG) == -1){
perror("listen");
exit(0);
}
/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
newfd = accept(fd, NULL, NULL);
if(newfd < 0){
perror("accept");
exit(0);
}
ClientHandle(newfd);
close(fd);
return 0;
}
int ClientHandle(int newfd){
int file_fd = -1;
char buf[BUFSIZ] = {};
int ret;
do {
ret = recv(newfd, buf, BUFSIZ, 0);
}while(ret < 0 && errno == EINTR);
if(ret < 0){
perror("recv");
exit(0);
}else if(ret == 0){
close(newfd);
return 0;
}else{
printf("=====================================\n");
printf("%s", buf);
fflush(stdout);
}
bzero(buf, ret);
file_fd = open(HTTPFILE, O_RDONLY);
if(file_fd < 0){
perror("open");
exit(0);
}
ret = read(file_fd, buf, BUFSIZ);
printf("%s\n", buf);
send(newfd, buf, ret, 0);
close(file_fd);
bzero(buf, ret);
file_fd = open(HTMLFILE, O_RDONLY);
if(file_fd < 0){
perror("open");
exit(0);
}
ret = read(file_fd, buf, BUFSIZ);
printf("%s\n", buf);
send(newfd, buf, ret, 0);
close(file_fd);
close(newfd);
return 0;
}