一、网络通信相关函数
客户端:socket(); bind(); connect();send();recv();close();
服务端:socket(); bind(); listen(); accept(); recv(); send(); close();
epoll_create():
epoll_ctl():
epoll_wait():
fcntl():用来设置阻塞和非阻塞的
只要在Linux运行的语言 python java
strace ./ 追踪最后都会是上面这几个API接口,本质是linux内核已经封装后额可以实现百万并发
二、建立连接
1. socket();
插座、 插座 是两个东西:
功能:
a、分配fd, int值
b、tcb(tcp control block);创建新的tcb控制块
2. bind(fd, );
a、IP PORT 绑定在tcb中
如果客户端未进行绑定,即进行connect(),会自动创建分配一个端口,进行绑定ip port
3. listen(fd, backlog)
监听,创建tcb中状态设置为监听,对应的有状态迁移图
a、tcb->statuse = TCP_STATUS_LISTEN;
b、tcb->syn, 然后接收请求
4. 三次握手
第一次握手请求,TCP报文头syn位置为1,syn seqnum = 1234(标志位syn置为1 标志是个握手的包 一开始就是客户端端先发起, 1234是随机值,避免被人抓包,,告诉对方你从哪里发,不重复,不乱序,不丢)
第二次握手请求, ack 和syn 位同时置为1, ack 位置1, acknum = 1235, syn seqnum = 5646 (1235代表1235之前所有的值都受到了, 同时5646是随机值,告诉对方从哪里发)
第三次握手请求,ack位置1 acknum = 5647,**服务端fd是在accept中分配的**。
注意:
问题1 :tcp的连接的生命周期,是从什么时候开始的?
答:从客户端第一次握手就开始的,此时服务端连接已经是半连接队列syn队列,**但是fd是从accept()中分配的**
问题2:第三次握手的数据包,如何从办理按揭队列查找匹配的节点
答:是根据源ip ,源port进行匹配的,
ip来源于ip协议,port来源于tcp协议
问题3:syn泛洪(是来了大量的客户端请求进行连接,构成了大量syn队列)
答:这个是根据listen的第二个参数backlog来限制,主要由以下演化而来,
第一种
1、backlog就是syn队列(半连接)的长度数量(目前防火墙已经解决了,意义不大)
2、syn+accept队列的总长度中,未分配的fd的tcb数量
3、accept队列长度,待连接的数量(目前很多服务器是第三种方式来限制,可以大大提升建立的数量)
5. listenfd ---->accept() 和 clientfd ------>recv()/send()
作用:
1、分配出fd
2、取出fd进行映射fd<—>tcp连接
问题1:如果同时有三个半连接需要建立连接,
如果用ET边沿触发,每次只触发一次,即只能连接一个请求,其他两个连接无法建立,而LT水平触发可以一直触发,即三个连接全部建立。
问题2:如果用边沿触发,如何处理所有的连接 ?
建立while循环接收连接,当accept的返回值为-1时(**accept非阻塞,即立即会有返回值**),也就是无连接可以建立,此时break掉;
while(1){
fd = accept(); // 非阻塞的,也就是会立刻返回结果;
if(fd == -1){break;}
}
三、传输信息
1. send(fd, buffer, size_t, flag);
send(fd, buffer, 50, 0);
send(fd, buffer, 150, 0);
send(fd, buffer, 250, 0);
flag标志位:可以知道消息发送到哪一层链路,设置阻塞/非阻塞,将数据从应用层拷贝到内核层wmem(write memory)
发送三次,内核可能会根据MTU进行计算,小于MTU的内容,一次性发送过去,大于MTU的内容分多次发送,一般MTU大小1500bytes,只能在套接字处于连接状态的时候才能使用。(只有这样才知道接受者是谁)
2. recv(fd, buffer, size_t, flag);
recv(fd, buffer, 250, 0);
recv(fd, buffer, 250, 0);
recv(fd, buffer, 150, 0);
从协议栈(read memory)copy应用层软件对应的地址, flag默认为0
100:recv(fd, buffer, 1000, 0);
101:for(int i= 0; i < 10; i++)
{
recv(fd, buffer, 100, 0);
}
*两种接收方式差别不大,只有当rmem缓冲区较小时,但是如果第一次接收100 rmem还有900尚未接收,即剩余接收也只能接收100,此时会有差别,但是rmem大小一般较大,比如2048,即大的话就不影响后续接收*
tcp协议用来描述两个主机进行传输的过程;
posix协议栈是研究应用软件到内核这个过程。
传输过程中,有五个关键词:
慢启动:将cwnd(拥塞窗口)的大小增加1,下一次就是cwnd是上一次的2倍,也就是指数增长,开始传输指数性增长
拥塞控制;增长到一定值,开始线性增长,一旦发现堵塞后,cwnd直接砍掉一半,此时cwnd这个窗口设置为1.
滑动窗口:我们总是希望数据发送的又快又多。但是这样,接收方可能来不及接收。所以要进行流量控制。发送过去的内容,需要对端确认;两根指针进行滑动,选择一段进行继续发送
延迟确认:接收方进行延迟回复ACK,即接收方的应用程序可以多消耗rmem中的内容,空出来更多,提高效率
超时重传:超过一定时间,未收到ack进行重传。
四、断开连接-四次挥手
close():不是网络函数,是文件函数(应用程序函数)
作用:
1、回收fd
2、fin关闭连接请求,发送一个空包
四次挥手特殊两种情况:
1、ack没有收到,先收到fin,
2、双方同时调用 close (服务器端出现大量time_wait)
三次握手特殊情形:
双方同时建立通信,没有客户端和服务端(p2p PeerToPeer,用在点对点 遥控器不通过服务端,直接和遥控器和空调相连接)
代码一样,绑定一样内容:
fd = socket();
bing(8000);
connect();