运输层UDP、TCP网络概念辨析以及socket编程流程图示

OUTLINEhtml

  • udp和tcp区别linux

  • udp碰见包丢失怎么办,设计一下编程

  • udp的包若是出现错误怎么办?上层保证吗安全

    • 差错检测能够百分百检错吗
    • 有哪些校验和计算方法
  • TCP如何保证可靠传输、沾包服务器

  • Linux下高并发socket最大链接数网络

    • linux下高性能网络框架,一个服务器连成千上万socket怎么办
  • RFID项目中服务器须要连多个客户,如何receive消息。并发

1 UDP和TCP区别

网络层为主机之间提供逻辑通讯,运输层为应用进程之间提供端到端的逻辑通讯。[1]框架

根据应用程序的不一样需求,运输层须要两种协议:dom

  • 面向链接的TCP(transmission control protocol)协议
  • 无链接的UDP(User datagram protocol)协议

二者区别在于:异步

  • UDP是无链接的,TCP是面向链接的
  • UDP尽最大努力交付报文,TCP保证可靠交付。
  • UDP是基于报文的,并不合并或拆分上层交下来的报文,而是添加首部后交给IP层。TCP是基于byte流的。

[2]从更多的角度比较:

UDP是message-based无链接协议,也就是发送数据以前不须要创建专用的端到端链接。传输数据的时候也不须要检查接收方的readiness和状态。

  • 1)不保证交付

UDP message发送以后便没法知道其状态了,有可能顺利到达接收方,也可能丢失。UDP没有确认收到、重传、超时这些概念。

  • 2)无序的
  • 3)轻量级,只是在IP的数据报服务之上增长了不多一点功能[1]
  • 4)datagrams

Packets相互独立发送,而且各自有明确的边界。若是发送方一个packet,那接收方必定原样收到。

  • 5)无拥塞控制

UDP自己不提供拥塞控制,若有必要,拥塞控制方法应该在应用层实现。

  • 6)支持广播

而TCP是connection-oriented协议。意味着通讯以前必须经过三次握手创建端到端链接。链接创建后,双方均可以经过这个链接发送数据。特色是

  • 1)可靠交付

这里的可靠只限于传输层。在应用层,仍然须要一个独立的acknowledgement flow control。

TCP管理message确认、重传和超时。

这里提到If it gets lost along the way, the server will re-request the lost part.这和[1]中提到可靠传输过程不符: 接收方不须要请求重传某个出错的分组。由于发送方每发完一个分组会设置一个超时计时器,若是超时计时器到期,发送方没有收到接收方对前一个分组的确认。那么发送方会自动重传。

在TCP中只有两种状况:1. 全部数据正确发送 2. 网络已经断掉,超时屡次。

  • 2)有序的
  • 3)重量级,支持可靠交付和拥塞控制
  • 4)数据以stream的方式读入。没有message(segment)的明显边界

1.1 辨析data、packet、datagram、message、segment、frame

data的范围最广,什么均可以叫作data,说发送data确定是不会错的。

首先讨论这些名词的联系,他们均可以叫作PDU(protocol data unit)[3],也就是通讯协议使用的数据单元。

[1]中使用的是TPDU(transport protocol data unit)。

因此他们的区别在于所在的协议不一样。

在传输层叫segment(报文段)

在网络层叫datagram(数据报)

在数据链路层叫frame(帧)

他们能够统称为packet(分组)。

我的认为message和packet同义,好比[3]中这几句能够证实:

But in other cases the term "segment" includes the whole TCP message, including the TCP headers. A single whole IP message is a "datagram". The original IP RFC refers to link-layer messages as "packets".

2 包丢失的处理

UDP不保证可靠交付,像拥塞控制同样,若有须要应该由应用层实现。

3 差错检测

数据从发送方到接收方可能会产生一些随即错误,差错检测指检测出错误数据[4]。

而纠错的方式有两种:

  • 1)Automatic repeat request (ARQ),网络中用的这种。
  • 2)Forward error correction (FEC)

差错检测方式(Error detection schemes)包括:

  • 1 Repetition codes
  • 2 Parity bits
  • 3 Checksums
  • 4 Cyclic redundancy checks (CRCs)
  • 5 Cryptographic hash functions
  • 6 Error-correcting codes

其中checksum是经过某种算术公式把原始数据算出算术和。

CRC是不安全的哈希函数,用来检测数据中随机错误。

UDP和TCP的校验和不只要对整个IP协议负载(包括UDP/TCP协议头和UDP/TCP协议负载)进行计算,还要先对一个伪协议头进行计算:先要填充伪首部各个字段,而后再将UDP/TCP报头及以后的数据附加到伪首部的后面,再对伪首部使用校验和计算,所获得的值才是UDP/TCP报头部分的校验和。[5]

具体计算方法是:

1)首先把校验和字段清零;

2)而后对每 16 位(2 字节)进行二进制反码求和;

反码求和时,最高位的进位要进到最低位,也就是循环进位。

接收方进行校验:按二进制反码求这些16bit字的和。若不差错,结果应为全1

4 TCP可靠传输

TCP下面的网络提供的是不可靠的传输,因此TCP必须采起适当的措施使得两个运输层之间的通讯变得可靠。[1]

传输过程可能出现两方面问题:

  • 信道产生差错
  • 接收方来不及处理收到的数据

TCP经过中止等待协议、滑动窗口来规避以上问题。

其中,中止等待协议指每发送完一个包就中止发送,等待对方确认。 中止等待协议的设计采用超时重传机制。

连续ARQ协议是滑动窗口的简化模型。

附:链接创建三次握手,链接释放四次挥手示意图。

5 Linux下高并发socket最大链接数

5.1 限制最大链接数的一些因素,以及去除各类限制的方法。[6]

去除各类限制后,理论上单独一个进程最多能够同时创建60000多个TCP客户端链接。

注意,做者server端口范围方面的观点有误,具体见5.2

原文摘要:

  • 1)用户进程可打开文件数限制

在Linux平台上,不管编写客户端程序仍是服务端程序,在进行高并发TCP链接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是由于系统为每一个TCP链接都要建立一个socket句柄,每一个socket句柄同时也是一个文件句柄)。

$ ulimit -n
1024

这表示当前用户的每一个进程最多容许同时打开1024个文件

  • 2)网络内核对TCP链接的有关限制

    • 内核对本地端口号范围的限制(误)
    • Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP链接数的限制
  • 3)使用支持高并发网络I/O的编程技术

在Linux上编写高并发TCP链接应用程序时,必须使用合适的网络I/O技术和I/O事件分派机制。

可用的I/O技术有同步I/O,非阻塞式同步I/O(也称反应式I/O),以及异步I/O。

在高TCP并发的情形下,若是使用同步I/O,这会严重阻塞程序的运转,除非为每一个TCP链接的I/O建立一个线程。可是,过多的线程又会因系统对线程的调度形成巨大开销。所以,在高TCP并发的情形下使用同步 I/O是不可取的,这时能够考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select(),poll(),epoll等机制。异步I/O的技术就是使用AIO。

从I/O事件分派机制来看,使用select()是不合适的,由于它所支持的并发链接数有限(一般在1024个之内)。若是考虑性能,poll()也是不合适的,尽管它能够支持的较高的TCP并发数,可是因为其采用“轮询”机制,当并发数较高时,其运行效率至关低,并可能存在I/O事件分派不均,致使部分TCP链接上的I/O出现“饥饿”现象。而若是使用epoll或AIO,则没有上述问题(早期Linux内核的AIO技术实现是经过在内核中为每一个 I/O请求建立一个线程来实现的,这种实现机制在高并发TCP链接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中,AIO的实现已经获得改进)。

综上所述,在开发支持高并发TCP链接的Linux应用程序时,应尽可能使用epoll或AIO技术来实现并发的TCP链接上的I/O控制,这将为提高程序对高并发TCP链接的支持提供有效的I/O保证。

5.2 [6]的错误在于服务端程序只侦听一个端口,并发链接数量与server可用端口数无关

[6]末尾7楼评论也指出了这个错误。标识一条连接是五个部分组成的:[协议,本地ip,本地端口,远端ip,远端端口]。对于服务器而言只用一个端口就能够承载多个连接

[10]对一个TCP服务程序能够支持多少并发 TCP 链接进行了严密的讨论,并给出程序测试。我认为这个是正确答案。

一个进程同时打开的文件数目的最大值在实践中是正确的,不符合原题意。

抛开操做系统层面,只考虑 TCP/IP 层面。创建一个 TCP 链接有开销仅仅是三次握手过程当中发送一些报文而已。

TCP 链接是虚拟的链接,不是电路链接,维持 TCP 链接理论上不占用网络资源(会占用两头程序的系统资源)。只要链接的双方认为 TCP 链接存在,而且能够互相发送 IP packet,那么 TCP 链接就一直存在。

因此单独谈论“TCP 并发链接数”是没有意义的,由于链接数基本上是要多少有多少。更有意义的性能指标或许是:“每秒钟收发多少条消息”、“每秒钟收发多少字节的数据”、“支持多少个活动的并发客户”等等。

该问题最终答案:

在只考虑 IPv4 的状况下,并发数的理论上限是 2^48(客户端 IP 的上限是 2^32 个,每一个客户端IP发起链接的上限是 2^16)。考虑某些 IP 段被保留了,这个上界可适当缩小,但数量级不变。实际的限制是操做系统全局文件描述符的数量,以及内存大小。

6 RFID项目中对多个socket的处理

6.1 一个完整的TCP客户-服务端程序须要的基本socket函数

在APUE中[7],C++版本socket函数以下:

  • 1 int socket(int domain, int type, int protocol);

domain: AF_INET/AF_INET6/AF-UNIX/AF_UPSPEC 协议域,又称协议族

type: SOCK_DGRAM / SOCK_RAW / SOCK_SEQPACKET / SOCK_STREAM 规定数据的传输方式

protocol: IPPROTO_IP/IPPROTO_IPV6/IPPROTO_ICMP/IPPROTO_RAW/IPPROTO_TCP/IPPROTO_UDP 指定为因特网域套接字定义的协议,一般为0,自动选择type类型对应的默认协议。

  • 2 int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

把本地协议地址赋给socket

  • 3 int listen(int sockfd, int backlog); //only called by server

backlog 指定最大容许接入的链接数量。

  • 4 int connect(int sockfd, const struct sockaddr *addr, socklen_t len); //only called by client
  • 5 int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len); //only called by server
  • 6 int close(int fg);
  • 7 int shutdown(int sockfd, int howto);

对于于[8]和[9]中的C#版函数以下:

  • 1 Socket(AddressFamily, SocketType, ProtocolType);
  • 2 socketobj.Bind(IPEndPoint);
  • 3 socketobj.Listen(int);
  • 4 socketobj.Connect(IPEndPoint);
  • 5 socketobj.Accept();
  • 6 关闭并释放资源
socketobj.Shutdown(SocketShutdown.Both);
socketobj.Close();

6.2 [8]和[9]原理图示

[8]是单用户模式,server只能连一个client,当链接创建后,server便不在监听。

双方都须要两个线程:一个用来接收报文,另外一个用来发送报文。咱们分别用Threc和Thsend表示

要改为[9]中的多用户模式只须要保持server的监听状态,接着对每一个新链接成功的client,存储其配套的socket、Threc、Thsend。

假设有c一、c二、c3三个client链接到server,会创建三个socket:connSocket1,connSocket2,connSocket3

socket为client和server提供endpoint到endpoint的通讯。其中endpoint格式为:ip:port

三个socket在server端的endpoint相同,在client端的endpoint不一样(ip相同,port不一样)

因此咱们能够用client端的endpoint来标识每一个socket。只须要把endpoint做为key,socket、Threc、Thsend做为value存在字典里面就能够灵活处理每一个每一个socket链接了。

References

相关文章
相关标签/搜索