学习笔记-linux网络编程2

IP数据报:最多就1460字节,因为除去了头部

这个图中的ACK 1001,意思是1001之前的数据都收到了,mss,最大的数据上限,8000随便起的

连接的过程:

 

三次握手:主动发起连接请求端,发送SYN标志位,请求建立连接.携带数据包包号,数据字节数(0),滑动窗口大小

被动接受连接请求端,发送ACK标志位,同时携带SYN请求标志位,携带序号,确认序号,数据字节数(0),滑动窗口大小

主动发起连接请求端,发送ACK标志位,应答服务器连接请求.携带确认序号

 

客户端发送SYN 500,服务器端接受连接,那么回复ACK501,确认收到,顺便携带ACK700,客户端回复是ACK701,则建立连接,这些都是在内核完成的,体现在用户空间的是accept 和 connect 的返回成功与否

如图,数据传输的第一个1001,一般是按照下图中的服务器端上一个回复的1001开始的,ACK的8001是保险起见,防止上一次的ACK8001没有送到服务器端,不然的话服务器就无法知道是否成功建立起三次握手

确认的过程就是发送一个标志位,然后服务器端回复一个ACK携带数据的终止节点,比如1025(1024),收到就回复ACK2049

 

断开连接,就是四次挥手:

客户端作为主动的一方,是主动关闭连接的,

 

半关闭:客户端,将写的缓冲区关闭,读保留,因为有半关闭,所以有四次

挥手是:客户端说,我准备挂电话,不说话了,服务器端说,行,别说话了,然后,服务器又说,我也没有啥想说的了,既然你没有可说的,就结束了吧,客户端说:结束吧,四次挥手就这样结束了,

客户端在收到服务器端说可以允许我不说话的时候,就把写的缓存关了,因为服务器可能还有话说,所以读缓存留着,等服务器也不想说话了,这时候确认不会再读到数据,才把写缓存关了,因为,这个过程就很安全.

四次挥手:主动关闭连接请求端,发送FIN标志位

被动关闭连接请求端,应答ACK标志位 ------半关闭完成

被动关闭连接请求端,发送FIN标志位

主动关闭连接请求端,应答ACK标志位

滑动窗口:

发送给连接请求端.本端的缓冲区大小(实时),保证数据不会丢失.

多进程服务器:

 

 一个进程负责接收,然后创建进程进行连接

 伪代码:

  1. socket
  2. bind
  3. listen
  4. while (1)

{

cfd = accept();

pid = fork();

if(0 == pid)

{

close(lfd) //不需要,关闭

read()

业务处理

write()

}else if(pid > 0)

{

close(cfd);

while(1)

{

waitpid(0, NULL, WNOHANG);

}

continue;

}

}

  1. (子进程,为了子进程被init接收,父进程只能死,但是为了能处理以后的请求,不能死

,如果使用while 一直waitpid,也是无法处理新的请求,不回收子进程死亡就会变僵尸,占用资源),因此用信号来进行处理

注册信号捕捉函数: SIGCHLD

在回调函数中,完成子进程的回收

while(waitpid())

多线程服务器:

 伪代码:

1.socket

2.bind

3.listen

4.while (1)

{cfd = accept

pthread_create(&tid, NULL, tfn, NULL )}

5.也要做相应的回收操作,不回收就会有僵尸线程,

可以两个方式回收,一个是detach分离开来,一个是专门建立一个线程负责将这些线程回收;

(兄弟进程,跨代进程间不能互相回收,兄弟线程可以)

封装socket的接口,将错误处理都放在里面,这样增加主要业务代码的逻辑清晰度

TCP状态:主动发起连接

CLOSE -- 发送SYN -- SEND_SYN -- 接受ACK. SYN -- SEND_SYN -- 发送ACK -- ESTABLISHED

 

对应的状态图,使用命令netstat -apn 可以进行监控

 

TCP状态:主动关闭连接:

ESTABLISHED -- 发送FIN -- FIN_WAIT_1 -- 接收ACK -- FIN_WAIT_2 (半关闭) -- 接收端发送FIN -- FIN_WAIT_2(半关闭) -- 回发ACK -- TIME_WAIT(只有主动关闭的连接方才会经历这个状态) -- 等2MSL时长 -- CLOSE

如果服务端先关闭了,再关客户端,客户端就会等回复,就会出现问题,等到这个超时结束

 

TCP状态:被动接收连接:

CLOSE 很快变成LISTEN

CLOSE -- LISTEN -- 接收SYN -- 发送ACK .SYN -- SYN_RCVD -- 接收ACK (如果收不到,就一直发) -- ESTABLISHED

TCP状态:被动关闭连接:

接收到FIN 发送ACK -- CLOSE_WAIT  

ESTABLISHED -- 接收对端发送的 -- 接收FIN -- ESTABLISHED -- 发送ACK -- CLOSE_WAIT(主动关闭连接端处于半关闭) -- 发送FIN -- LAST_ACK

2MSL时长的意义:

 

如果发送的ACK没到被动关闭端怎么办,所以等一下,如果被动关闭端没有收到这个ACK,会重复发送FIN,但是如果等太久就失去意义了,因此就是共40s

端口复用函数:

setsockopt

定义一个opt = 0/1 是否生效,启用端口复用

端口复用,不用再等待2MSL,用在bind之前,绑定端口之前进行复用,他没有改变那个等待的动作,只是让这个端口可以被复用而已

半关闭:通信双方中只有一端关闭通信,

shutdown,close只是减少描述符的一个引用,引用为0,的时候,就关闭,而shutdown则是直接关闭这个描述符

select:

select负责监听,操作accept仍然交给server

 

select参数:

原理:借助内核,select监听,客户端连接.数据通信服务

FD_ZERO 清空文件描述符集合

FD_SET 等待监听的文件描述符集合

FD_CLR 文件描述符从集合里面移除

FD_ISSET判断文件描述符是否在集合里面

select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval * timeout)

nfds ..

readfds..

exceptionfds ..

timeout > 0 :设置监听超时时长

NULL: 阻塞监听

0 : 非阻塞监听,轮询

返回值:

 > 0 :所有监听集合中,满足对应事件的总数

0: 没有满足监听条件的文件描述符

-1 : error

传入传出参数的实例图片:

 

思路:

lfd = socket()

bind();

listen();

fd_set rset, allset;

FD_ZERO(&rset);

while(1)

{

rset = allset

ret = select(lfd+1, &rset, NULL,NULL,NULL);

if(ret > 0)
{

if(FD_ISSET(lfd, &rset))

{

cfd = accept();

FD_SET(cfd, &allset);

}

for(i=lfd+1;i<=最大文件描述符;i++)

{

FD_ISSET(i, &rset);

read()

//业务处理

write();

}

}

}

如果读不到sock fd的文件,read返回0,可以当成是网络已经关闭了

select缺点:如果一个client fd 就是1023,那么就得轮询判断

优缺点:

缺点:监听上限:监听上限受问间描述符上限,最大1024

检测满足条件的fd,自己添加业务逻辑提高小,提高编码难度

优点:跨平台,win.linux.macOS,Unix,类unix.mips,

添加自定义的数组,提高select的效率:

 

通过红色的数组存fd信息,减少循环遍历的时间

在处理读写的fd的时候,如果只有一个读写的fd返回,那么处理完一次之后进行break对fd数组的查询,也可以节省一定的时间

多路IO转换:

select:

poll:(时代的本成品,了解一下)

int poll(struct pollfd *fds, neds_t nfds, int timeout)

fds:监听的文件描述符[数组]

struct pollfd

{

int fd;待监听的文件描述符

short events:待监听的文件描述符对应的监听事件,取值POLLIN,POLLOUT,POLLERR

short revents 传入时是0,如果满足对应事件,返回非0

}

nfds:监听数组的,实际监听个数,

timeout:超时时长

>0:超时时长

-1:阻塞等待

0,不阻塞

返回值:返回满足对应监听事件的个数

poll的示意图:

 

使用的代码实例:

 

poll的优缺点:

优点:自带数组结构,可以将 监听事件集合 和 返回事件集合 分离.

拓展监听上限超出1024.

缺点,不能跨平台,只能在linux,类unix平台使用,

epoll 

突破1024文件描述符限制:

cat /proc/sys/fs/file-max 查看当前的计算机能打开的最大文件个数,

可以修改 /etc/security/limits.conf 修改

epoll是一个红黑树

操作函数

int epoll_create(int size)

size:创建一个数量,但是这个数量是给内核进行参考的

返回值:成功,指向新创建的红黑树的根节点

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epfd 创建的返回值

op: 对该监听红黑树所做的操作,

EPOLL_CTL_ADD 添加fd到监听红黑树

EPOLL_CTL_MOD 修改监听事件

EPOLL_CTL_DEL 取消对fd的监听

fd 等待监听的fd

event : 本质struct epoll_event 结构体地址

events:

EPOLLIN/EPOLLOUT/EPOLLERR

data:联合体

int fd; 对应监听事件的fd

void *ptr; 指针

uint32 u32:

uint u64 u64;

返回值:

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 阻塞监听

epfd:..

events:数组,传出参数,满足监听条件的fd结构体,

maxevents:数组的大小,

timeout:

负 0 正

返回值:

>0 满足监听的总个数,可以用作循环上限

0: 没有fd满足监听事件

-1: 错误码

创建根节点,加上一个节点,行为,fd,结构体fd(包含动作,fd),wait行为,数组,还有这个数组的大小,还有阻塞时间

epoll对于多连接,但是只有少量进行发送的情况下,非常适合这个场景

EL和LT模:

缓存区有数据,读走一半,剩下的一半的能触发,叫做LT,水平触发(默认)

不能触发,叫做ET,边缘触发

 

ET高效模式,但是只支持非阻塞模式

使用:

struct epoll_event event;

event.events = EPOLLIN | EPOLLET;

epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);

int flg = fcntl(cfd, F_GETFL);

flg |= O_NONBLOCK;

fcntl(cfd, F_SETFL, flg);

优缺点:

优点: 高效.能突破1024文件描述符

缺点: 不能跨平台

epoll反应堆模型

自动回调:反应堆

原来:socket.bind.listen -- epoll_create 创建监听红黑树 -- 返回epfd -- epoll_ctl()向树上添加一个监听fd -- epoll_wait 监听 -- 对应监听fd事件发生 -- 返回监听满足数组. -- 判断返回数组元素 -- lfd满足 --Accept -- cfd满足 -- read() -- 业务 -- write()

反应堆:不但监听cfd的读事件,还要监听cfd的写事件

........--业务处理 -- cfd从监听红黑树摘下 -- EPOLLOUT -- 回调函数 -- epoll_ctl() -- EPOLL_CTL_ADD重新放到红黑树上监听写事件 -- 等待epoll_wait返回 -- 说明可写 -- write

-- cfd从监听红黑树摘下 -- EPOLLIN -- epoll_ctl() -- EPOLL_CTLADD重新放到红黑上监听事件 -- epoll_wait监听

线程池模型:

减少线程创建的时间,提前进行设置

TCP通信和UDP通信各自的优缺点:

TCP:面向连接的,可靠数据报传输.对于不稳定的网络层,采取完全弥补的通信方式.丢包重传

优点:

稳定.数据流量稳定.速度稳定.传递的顺序,

缺点:传输速度慢,效率低,开销大

使用的场景:数据的完整性要求高,不追求效率.大数据传输,文件传输.

UDP:无连接,不可靠的数据报传输,对于不稳定的网络层,采取完全不弥补的方式,默认还原网络状况

优点:传输数据大,效率大,开销小

缺点:不稳定,数据包流量比较差.速度.顺序

使用场景:对时效性要求较高,稳定性其次,游戏.视频会议,视频电话

腾讯.华为.阿里 -- 应用层数据校验,弥补udp的不足

UDP实现的C/S模型:

由于省去了三次握手 那么accept和connect可以省略不计

recv()/send() 只能用于TCP通信

server:

lfd = socket(AF_INET, DGRAM,0);

bind(); ip地址,端口号

listen可有可无

while(1)

{

read被替换 -- recvfrom()

业务处理;

write( )-- 被替换成sento()

}

close()

client:

connfd = socket(AF_INET, SOCK_DGRAM, 0)

sendto(‘服务器地址结构’, 地址结构大小);

recvfrom

写到屏幕

close;

recvfrom:

sockfd:监听的fd

buf:缓冲区地址

len:缓冲区长度

flags:0

src_addr:传出参数,&addr传出,对端的地址结构

addrlen:传入传出参数

返回值:成功就是返回字节数,失败的话就是负1的实现

sendto:

sockfd:..

buf:..

len:..

flags:0

src_addr:传入,目标的地址结构

addrlen:目标地址结构的长度

udp的bind无用  ,端口都用的addr地址结构的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值