1、tcp和udp的区别?
(1) tcp面向连接(如打电话要先拨号建立连接);udp是无连接的,即发送数据之前不需要建立连接
(2) tcp提供可靠的服务。也就是说,通过tcp连接传送的数据,无差错,不丢失,不重复,且按序到达;udp尽最大努力交付,即不保 证可靠交付
(3) tcp面向字节流,实际上是tcp把数据看成一连串无结构的字节流;udp是面向报文的
udp没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如ip电话,实时视频会议等)
(4) 每一条tcp连接只能是点到点的;udp支持一对一,一对多,多对一和多对多的交互通信
(5) tcp首部开销20字节;udp的首部开销小,只有8个字节
(6) tcp的逻辑通信信道是全双工的可靠信道,udp则是不可靠信道。
2、udp调用connect有什么作用?
(1)、因为udp可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom()
时都必须指定目标ip和端口号。通过调用connect()建立一个端到端的连接,
就可以和tcp一样使用send()/recv()传递数据,而不需要每次都指定目标ip和端口号。
但是它和tcp不同的是它没有三次握手的过程。
(2)、还可以通过在建立连接的udp套接字,再次调用connect()实现以下功能:
a.指定新的ip地址和端口号
b.断开连接
(3)、tcp只能调用一次connect()函数。
3、tcp的十一种状态?
(1) closed: 初始状态,表示tcp连接是关闭或者未打开
(2) listen: 表示服务端的某个socket处于监听状态,可以接受客户端的连接
(3) syn_rcvd: 表示收到了syn报文。这个状态基本上是服务器端收到syn报文后的一个短暂的中间状态
,使用netart很难捕捉到。当再次收到客户端的ack报文时,进入到established的状态。
(4) syn_sent: 这个状态与syn_rcvd状态相呼应,客户端主动发起连接,调用connect函数时,发送syn报文,
随即进入到syn_sent状态,并等待服务端的syn报文。当接收到服务端的syn报文后,回应ack后,会
进入到 established 的状态。
(5) established: 表示tcp已经建立成功。
(6) fin_wait_1 :这个状态得好好解释一下,其实fin_wait_1 和fin_wait_2 两种状态的真正含义都是表示等待对方的fin报文。
而这两种状态的区别是:fin_wait_1状态实际上是当socket在established状态时,它想主动关闭连接,
向对方发送了fin报文,此时该socket进入到fin_wait_1 状态。而当对方回应ack报文后,
则进入到fin_wait_2 状态。当然在实际的正常情况下,无论对方处于任何种情况下,都应该马上回应ack报文,
所以fin_wait_1 状态一般是比较难见到的,而fin_wait_2 状态有时仍可以用netstat看到
(7) fin_wait_2:上面已经解释了这种状态的由来,实际上fin_wait_2状态下的socket表示半连接,即有一方调用close()主动要求关闭连接。
注意:fin_wait_2 是没有超时的(不像time_wait 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),
那这个 fin_wait_2 状态将一直保持到系统重启,越来越多的fin_wait_2 状态会导致内核crash。
(8) time_wait: 表示收到了对方的fin报文,并且已经发送了ack。这个时候实际上已经没有了报文交互,那么time_wait状态
下的tcp连接会等待2*msl,然后回到close状态。(max segment lifetime,最大分段生存期,
指一个tcp报文在internet上的最长生存时间。每个具体的tcp协议实现都必须选择一个确定的msl值,
rfc 1122建议是2分钟,但bsd传统实现采用了30秒,linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值)。
如果fin_wait_1状态下,收到了对方同时带fin标志和ack标志的报文时,可以直接进入到time_wait状态,而无须经过fin_wait_2状态
(9) closing: closing 状态表示一方发送fin报文后,并没有收到对方的ack报文,反而却也收到了对方的fin报文。
什么情况下会出现此种情况呢?那就是当双方几乎在同时close()一个socket的话,就出现了双方同时发送fin报文的情况,
这是就会出现closing 状态,表示双方都正在关闭socket连接。
这种状态在实际情况中应该很少见,属于一种比较罕见的例外状态。正常情况下,当一方发送fin报文后,
按理来说是应该先收到(或同时收到)对方的ack报文,再收到对方的fin报文。
(10) close_wait:表示正在等待关闭。怎么理解呢?当对方close()一个socket后发送fin报文给自己,你的系统毫无疑问地将会回应一个ack报文给对方,
此时tcp连接则进入到close_wait状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话,
那你也就可以close()这个socket并发送fin报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,
继续发送或丢弃。简单地说,当你处于close_wait 状态下,需要完成的事情是等待你去关闭连接。加入对方一直不回应ack报文
,则socket会一直无法关闭。
(11) last_ack: 当被动关闭的一方在发送fin报文后,等待对方的ack报文的时候,就处于last_ack 状态。当收到对方的ack报文后,
也就可以进入到closed 状态了。
4、socket服务端的实现,select和epoll的区别?
(1) select缺点:
1.最大并发数限制:使用32个整数的32位,即32*32=1024来标识fd,虽然可修改,但是有以下第二点的瓶颈;
2.效率低:每次都会线性扫描整个fd_set,集合越大速度越慢;
3.内核/用户空间内存拷贝问题。
(2) epoll的提升:
1.本身没有最大并发连接的限制,仅受系统中进程能打开的最大文件数目限制;
2.效率提升:只有活跃的socket才会主动的去调用callback函数;
3.省去不必要的内存拷贝:epoll通过内核与用户空间mmap同一块内存实现。
5、epoll哪些触发模式,有啥区别?
(1) epoll 两种工作模式
水平触发:
内核中的socket接收缓冲区不为空,有数据可读,读事件一直触发
内核中的socket发送缓冲区不满,可以继续写入数据,写事件一直触发
边缘触发:
内核中socket接收缓冲区由空变为不为空,数据由不可读变为可读,事件触发(仅一次)。
内核中socket发送缓冲区由满变为不满,数据由不可写变为可写,事件触发(仅一次)。
(2) 工作模式选择时候,lt模式会一直触发可读可写事件,导致效率比较低。et模式由于读写事件
仅通知一次,可能会存在数据丢失的可能。
(3) 1.et模式时,当有多个连接同时到达服务器,epoll_wait会返回多个描述符,由于在et模式下就绪状态只返回一次,
因此为了防止漏掉连接,需要循环调用accept直到接收全部连接(即返回eagain错误)。
2. et模式时,在读写数据时,同样需要注意读写事件只触发一次的问题,若一次读或写没有处理全部数据,
则会导致数据丢失。解决办法是,accept接收连接时,设置连接的套接字为非阻塞,
并在读写数据时循环调用read/write直到数据全部处理为止(即返回eagain错误)。
3. lt模式时,若监控epoll out事件,由于内核缓冲区一开始时一直处于可写状态,会导致epoll_wait一直返回,降低效率。解决办法,
一开始不监听out事件,直接写数据直到写缓冲区满时(即返回eagain错误),再监听out事件,当数据全部写完时,就取消对out事件的监听
6、大规模连接上来,并发模型怎么设计?(高并发网络模型)
(1) linux的并发模型有三种,包括:多进程并发、多线程并发以及io复用模型。
(2)多进程并发:accept返回成功时候,就为这一个连接fork一个进程,专门处理这个连接上的数据收发,等这个连接处理结束之后就结束这个进程
所线程并发:类似多进程方式,但是针对一个连接启动一个线程。
优点: 相对多进程方式,会节约一些资源,会更加高效一些。
缺点: 相对多进程方式,增加了编程的复杂度,因为需要考虑数据同步和锁保护。另外一个进程中不能启动太多的线程。
在linux系统下线程在系统内部其实就是进程,线程调度按照进程调度的方式去执行的
select 多线程:有一个线程专门用于监听端口,accept返回之后就把这个描述符放入 描述符集合 fd中,一个线程用select去轮训描述符集合,
在有数据的连接上接收数据,另外一个线程专门发送数据。当然也可以接收和发送用一个线程。
epoll方式:一个线程专门进行端口监听,accept接收到连接的时候,把该连接设置成非阻塞模式,
把 epoll事件设置成边缘触发方式,加入到epoll管理。接收线程阻塞在epoll的等待事件函数。另外一个线程专门用于数据发送.
7、tcp结束连接怎么握手,time_wait状态是什么,为什么会有time_wait状态?哪一方会有time_wait状态,如何避免time_wait状态占用资源?
tcp通过四次挥手结束连接。
time_wait: 表示收到了对方的fin报文,并且已经发送了ack。这个时候实际上已经没有了报文交互,那么time_wait状态
下的tcp连接会等待2*msl,然后回到close状态。(max segment lifetime,最大分段生存期,
指一个tcp报文在internet上的最长生存时间。每个具体的tcp协议实现都必须选择一个确定的msl值,
rfc 1122建议是2分钟,但bsd传统实现采用了30秒,linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值)。
如果fin_wait_1状态下,收到了对方同时带fin标志和ack标志的报文时,可以直接进入到time_wait状态,而无须经过fin_wait_2状态。
time_wait状态是发起断连的一端存在的状态。
time_wait状态存在的意义:1、实现可靠的全双工连接:因为在最后一次发送ack报本可能丢失,则对端可能会希望本端复位重发,导致错误产生。
2、允许老的重复的分段在网络上消失。
8、tcp头多少字节?哪些字段?
tcp头有20字节,包括选项时会增大,最大不超过60字节。包括:16位源端口号、16位目的端口号、32位的序号、32位确认号、4位头部长度
标志位、16位窗口大小、16位校验和、16位紧急指针。
9、什么是滑动窗口?(后续补充)
tcp协议里窗口机制有2种:一种是固定的窗口大小;一种是滑动的窗口。这个窗口大小就是我们一次传输几个数据。
对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才允许被发送;
同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才允许接收。这样通过调整发送方窗口和接收方窗口的大小可以实现流量控制。
tcp滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。
10、connect会阻塞,怎么解决?
connect调用的时候,当发起主动连接的时候,如果服务端关闭,则进程会被connect阻塞,等待较长时间返回。假如直接将connect直接定义
为非阻塞的,则无法确定connect是否连接成功。
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4.使用select()检查该socket描述符是否可写
5.根据select()返回的结果判断connect()结果
6.将socket设置为阻塞模式
11、如果select返回可读,结果只读到0字节,什么情况?
之前处理过这么一个问题,就是socket一直返回可读,导致任务死循环。这个问题最后的结果是因为socket发生错误,但是没有读取错误码
导致socket一直可读。
12、keepalive 是什么东东?如何使用?
tcp协议中有长连接和短连接之分。短连接在数据包发送完毕后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即通常所说的
keepalive功能。
当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ack的响应包),select会返回socket可读,并且在recv时返回-1,
同时置上errno为etimedout。此时tcp的状态是断开的。
13、列举你所知道的tcp选项,并说明其作用?
14、socket什么情况下可读?
下列四个条件中的任何一个满足时,socket准备好读:
1.socket接收缓冲区中已经接收的数据的字节数大于等于socket接收缓冲区低潮限度的当前值;
对这样的socket的读操作不会阻塞,并返回一个大于0的值(即:准备好读入的数据的字节数).
我们可以用socket选项so_rcvlowat来设置此低潮限度,对于tcp和udpsocket,其缺省值为1;
2.连接的读这一半关闭(即:接收到对方发过来的fin的tcp连接).对于这样的socket的读操作将不阻塞,
并且返回0(即:文件结束符,fin包体长度为0字节);
3.socket是一个用于监听的socket,并且已经完成的连接数为非0.这样的soocket处于可读状态,
是因为socket收到了对方的connect请求,执行了三次握手的第一步:对方发送syn请求过来,使监听socket处于可读状态;正常情况下,
这样的socket上的accept操作不会阻塞;
4.有一个socket有异常错误条件待处理.对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的错误条件.
这些待处理的错误也可通过指定socket选项so_error调用getsockopt来取得并清除;
下列三个条件中的任何一个满足时,socket准备好写 :
1.socket发送缓冲区中的可用空间字节数大于等于socket发送缓冲区低潮限度的当前值,且(i):socket已连接(tcp socket),
或者(ii):socket不要求连接(如:udp socket).这意味着,如果我们将这样的socket设置为非阻塞模式,写操作将不会阻塞,并且返回一个正值
(如:由传输层接收的字节数).我们可以用socket选项so_sndlowat来设置此低潮限度,对于tcp和udp socket,其缺省值一般是2048bytes;
2.连接的写这一半关闭.对于这样的socket的的写操作将产生信号sigpipe;
3.有一个socket异常错误条件待处理.对于这样的socket的写操作将不会阻塞并且返回一个错误(-1),errno则设置成明确的错误条件.
这些待处理的错误也可以通过指定socket选项so_error调用getsockopt函数来取得并清除;