TCP_网络编程
TCP编程方式
服务端
//只说明大概流程,内部参数没有写详细
int fd = socket(); //创建套接字
bind(ip); //绑定当前的ip地址
listen(); //监听有谁连接
//listen(sockfd, 5)
//三次握手时,
//服务器会接收到2次数据,分别放入全连接队列和半连接队列两个队列中。
//linux中:两个队列的总和最大数
//uninx中:全连接队列的总和最大数
clientfd = accept(); //接受连接,返回在服务器端的代指所连接的客户端套接字
//第二个参数是:客户端的ip,实际上是网关的ip地址,而不是局域网的ip地址
recv(clientfd); //从客户端接收信息
send(clientfd); //给客户端发送信息
close(); //关闭套接字
客户端
bind();
connect(); //链接服务器端
send();
recv();
close();
recv < 0
1、io为非阻塞时,recv读空数据时,会返回-1
2、io为阻塞时,recvbuf中是空数据是不会返回的。
recv = 0
代表服务器和客户端断开了。
系统出现了大量close_wait是什么情况?
四次挥手时被动关闭方(服务器)收到fin包时,recv会返回0.
服务器主动调用close()函数时,客户端才会收到close_wait().
出现大量的close_wait,是我们调用recv返回0时,却没有及时调用close()造成的。
tcp中客户端与服务器有建立连接之后accept(),再进行交流。这样可以进行并发。但是udp如何进行并发呢?
UDP并发
背景:服务器端只有一个fd,却要处理多个客户端的数据。当成千上万个客户端数据过来时,udp服务器如何区分?如何同时做通讯?也就是如何做到多个客户端并发?
//udp服务器端流程
fd = socket();
recvfrom(fd,addr,buffer,...);
sendto();
方法一:
对buffer数据包加层稳定的协议,使其自带来处客户端的信息,表明身份。此方法的前提是数据是顺序接收的,
但是再公网条件下,udp难保证数据先发的先到,后发的后到。发送数据是无序的。此方法不可行。
方法二:仿照Tcp模式实现
创建socket,等待着迎宾,当其接收到第一个数据后,再创建独有的socket来与其专门通信。
socket是什么?
socket是什么?
本质是一个唯一的数字,其
由文件fd+网络资源数值化 (也就是五元组:源ip/端口 + 目ip/端 口 + 传输协议(tcp或者udp))组成。
fd 是一个socket句柄,可以看做是一个文件,在socket上收发数据,相当于对一个文件进行读写,所以一个socket句柄,通常也用表示文件句柄的fd来表示。
fd可当作文件描述符,也可当作客户端。
接下来介绍文件描述符的属性也就是io属性,也就是内存和外设的通信属性。
内核检测到文件读写就会发送sigIO。
什么是sigIO 信号
背景:网卡接收到数据就会通知操作系统发出sigIO消息。
那只要fd读写就发出sigIO信号,那TCP是否也可以实现do_sigio处理信号函数?
理论上tcp可发出,但是不建议这么做,因为与dup相比,tcp处理数据时产生了更多的信号。
sigIO 信号如何工作的?(面试)
从三点回答:
1、进程的信号如何保存?
2、信号如何保存在进程中?
3、信号如何发送的,或者进程如何捕获到信号的?
信号接收的流程是什么?
1、进程结构体task_struct中的sighand_struct,action数组中存储的就是各种io信号。每个进程中有结构体sigaction数组,叫action,大小为64,里面存储了信号集合
2、有信号时系统调用内核函数将sigIO信号,就会将存储再action[sigIO-1]位置处;
3、当给某个进程发信号时,进程action[sigIO-1]等待的条件满足后,就去执行对应的回调函数。
kill实际调用kill_something_info(pid, sig)pid是进程,sig是信号,函数就是给哪个进程发什么信号。走到最里面的话send_signal、最后走到signalfd_notify()通知的意思,里面是个中断其实就是信号等待。
就是发送sigio时,进程中action中等待对应的sigio,等待条件满足后,就去执行对应的回调函数。
select/poll/epoll
背景:处理多种信号
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间.
参考二
seleect
三个集合
select();
//参数:
//总数量IO,个数为MAX(fd)+1
//可读的IO集合,
//可写的IO集合,
//出错的IO集合,
//timeout,轮询的时间
poll
一个集合
poll();
//参数
//pfd,
//length, pfd数组长度,
//timeout
epoll
epoll_create();
epoll_ctr();
epoll_wait();
//int nready = epoll_wait(epfd, events, 512, -1);
//第二个参数:能够检测到事件的容器;
//第三个参数:容器的容量,也就是最大一次性检测多少个事件;
//第四个参数:多久轮询检测一次,-1代表有数据来才检测