网络是如何连接的(三)

前言

上一章我们讨论了TCP模块是如何处理数据收发的,接下来我们讨论IP与以太网的包收发操作以及UDP协议收发数据操作

1、关于包

1.1、包的基本概念

TCP模块的各个阶段都需要委托IP模块将数据封装成包发给通信对象,包是由头部和数据两部分组成的,如图

头部里包含目的地址的控制信息,头部后面就是委托方要发送的数据。发送方的网络设备会负责创建包,创建包的过程就是生成含有控制信息的头部,然后在附加上要发送的数据。接下来包会被发往附近的网络设备,在网络设备中会存在一张表,网络设备根据头部的控制信息在表中查找接下来应该发往哪个设备,在到达下一个设备后,下一个设备照葫芦画瓢继续转发。这样,经过层层转发后,网络包就被发送到了目的地。

IP和TCP网络就是对上述结构的扩展,但是更加复杂。在TCP/IP网络中,包含路由器和集线器两种设备,路由器根据IP规则判断下一个路由器的位置,集线器根据以太网协议将包转发到下一个转发设备。

具体来说,TCP/IP包包含MAC头部(以太网协议)和IP头部(用于IP协议)。这两个头部分别具有不同的作用。首先,发送方将包的目的地,也就是要访问的服务器的IP地址写入IP头部中。这样一来,我们就知道这个包应该发往哪里,IP协议就可以根据这一地址查找包的传输方向,从而找到下一个路由器的位置。接下来,IP协议会委托以太网协议将包传输过去。这时,IP协议会查找下一个路由器的以太网地址(MAC地址),并将这个地址写入MAC头部中。这样一来,以太网协议就知道要将这个包发到哪一个路由器上了。网络包在传输过程中会经过集线器,集线器是根据以太网协议工作的设备。为了判断包接下来应该向什么地方传输,集线器里有一张表(用于以太网协议的表),可根据以太网头部中记录的目的地信息查出相应的传输方向。网络包会通过路由器到达下一个路由器。这个过程不断重复,最终网络包就会被送到目的地,当目的地设备成功接收之后,网络包的传输过程就结束了。可以看到,在上述过程中,IP和以太网是分工合作的,我们也可以将以太网换成别的通信网络如ADSL、FTTH、无线局域网等以适应不同的场景。

1.2、包收发操作概览

尽管我们说IP模块负责将包发给对方,但实际上将包从发送传输到接收方的工作是由集线器、路由器等网络设备来完成的A,因此IP模块仅仅是整个包传输过程的入口而已。即便如此,IP模块还是有很多工作需要完成,首先我们先粗略地整理一下。 如图

包收发操作的起点是TCP模块委托IP模块发送包的操作,这个委托的过程就是TCP模块在数据块的前面加上TCP头部,然后整个传递给IP模块,这部分就是网络包的内容。与此同时,TCP模块还需要指定通信对象的IP地址,也就是需要写清楚"将什么内容发给谁"。IP模块会将包的内容当成一整块数据,在前面加上包含控制信息的头部,包括IP头部和MAC头部。IP头部包含IP协议规定的,根据IP地址将包发往目的地址所需的控制信息,MAC头部包含通过以太网的局域网将包传输给最近路由器所需的控制信息。接下来封装好的包会被交给网卡,网卡会将包的数字信息(0和1)转成电信号或者光信号,通过网线或者光纤发送出去。然后这些信号就会到达集线器、路由器这些设备,在由这些设备一步步转发到目的地。

接收的过程和发送的过程是相反的,信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给IP模块。接下来,IP模块会将MAC头部和IP头部后面的内容,也就是TCP头部加上数据块,传递给TCP模块。接下来的操作就是我们之前讲过的TCP模块负责的部分了。

2、IP模块的工作过程

2.1、生成IP头部

IP模块接受TCP模块的委托生成IP头部,IP头部包含的内容如下所示,其中最重要的就是IP地址。

我们知道计算机可以拥有多块网卡,而IP地址实际上是分配给网卡的,此处发送方IP地址其实是发送包的网卡的地址,接收方的IP地址也是如此。那么该如何判断使用哪块网卡发送呢?这个判断相当于在多块网卡中判断应该使用哪一块网卡来发送这个包,也就相当于判断应该把包发往哪个路由器,因此只要确定了目标路由器,也就确定了应该使用哪块网卡,也就确定了发送方的IP地址。

我们使用路由表来判断,通过route print命令来显示路由表

首先,我们对套接字中记录的目的地IP地址与路由表左侧的Network Destination栏进行比较,找到对应的一行。例如,TCP模块告知的目标IP地址为192.168.1.21,那么就对应图中的第6行,因为它和192.168.1的部分相匹配。如果目标IP地址为10.10.1.166,那么就和10.10.1的部分相匹配,所以对应第3行。以此类推,我们需要找到与IP地址左边部分相匹配的条目,找到相应的条目之后,接下来看从右边数第2列和第3列的内容。右起第2列,也就是Interface列,表示网卡等网络接口,这些网络接口可以将包发送给通信对象。此外,右起第3列,即Gateway列表示下一个路由器的IP地址,将包发给这个IP地址,该地址对应的路由器A就会将包转发到目标地址。路由表的第1行中,目标地址和子网掩码A都是0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。这样一来,我们就可以判断出应该使用哪块网卡来发送包了,然后就可以在IP头部的发送方IP地址中填上这块网卡对应的IP地址。接下来还需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是TCP模块委托的内容,则设置为06(十六进制),如果是UDP模块委托的内容,则设置为17(十六进制),这些值都是按照规则来设置的。

其他字段内也需要填写相应的值,但对大局没什么影响。

2.2、生成以太网专用MAC头部

网络包最终是要交给以太网传的设备传输的。以太网在判断网络包目的地时和TCP/IP的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而MAC头部就是干这个用的。下图展示了MAC头部包含的内容。

值得注意的是,我们在以太类型这里只展示了IP协议的以太网类型,实际上,其他协议只要有相对应的以太类型,则都可以在以太网中传输。

这里的发送方地址,我们就填写发送网卡的MAC地址就行了,网卡的MAC地址在出厂时就已经被指定,网卡的驱动程序可以从ROM或配置文件中拿到。

2.2.1、通过ARP查询目标路由器的MAC地址

到这里我们还不知道目标路由器的MAC地址是什么,ARP(地址解析协议)就是用来干这个的。ARP是利用以太网内广播的形式,向所有子网内的设备提问,如图

如果路由表的设置正确,那么对方应该在同一子网,否则对方无法作出ARP响应,这时只能认为对方不存在,包的发送操作就会失败。而为了减少网络中的ARP包,避免每次发包都查询一次MAC地址,我们会将查询结果缓存在内存空间中备用,并每隔一段时间(通常是几分钟)过期一次。将MAC头部加在IP头部的前面,整个包就完成了。

到这里为止,整个打包的工作是由IP模块负责的。有人认为,MAC头部是以太网需要的内容,并不属于IP的职责范围,但从现实来看,让IP负责整个打包工作是有利的。如果在交给网卡之前,IP模块能够完成整个打包工作,那么网卡只要将打好的包发送出去就可以了。对于除IP以外的其他类型的包也是一样,如果在交给网卡之前完成打包,那么对于网卡来说,发送的操作和发送IP包是完全相同的。这样一来,同一块网卡就可以支持各种类型的包。

3、网卡的工作过程

3.1、认识以太网

以太网这个名词你可能经常听到,那到底什么是以太网呢。一言以蔽之,太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术,它的演进过程如下图所示

  • 原型
    从图上不难看出,这种网络的本质其实就是一根网线。图上还有一种叫作收发器的小设备,它的功能只是将不同网线之间的信号连接起来而已。因此,当一台计算机发送信号时,信号就会通过网线流过整个网络,最终到达所有的设备。不过,我们无法判断一个信号到底是发给谁的,因此需要在信号的开头加上接收者的信息,也就是地址。这样一来就能够判断信号的接收者了,与接收者地址匹配的设备就接收这个包,其他的设备则丢弃这个包,这样我们的包就送到指定的目的地了。为了控制这一操作,我们就需要使用表2.3中列出的MAC头部。通过MAC头部中的接收方MAC地址,就能够知道包是发给谁的;而通过发送方MAC地址,就能够知道包是谁发出的;此外,通过以太类型就可以判断包里面装了什么类型的内容。
  • 中继式集线器
    这个原型后来变成了(b)中的结构。这个结构是将主干网线替换成了一个中继式集线器B,将收发器网线替换成了双绞线C。不过,虽然网络的结构有所变化,但信号会发送给所有设备这一基本性质并没有改变。
  • 交换式集线器
    个结构看上去和中继式很像,但其实里面有一个重要的变化,即信号会发送给所有设备这一性质变了,现在信号只会流到根据MAC地址指定的设备,而不会到达其他设备了。当然,根据MAC地址来传输包这一点并没有变,因此MAC头部的设计也得以保留。

尽管以太网经历了数次变迁,但其基本的3个性质至今仍未改变,即将包发送到MAC头部的接收方MAC地址代表的目的地,用发送方MAC地址识别发送方,用以太类型识别包的内容。因此,大家可以认为具备这3个性质的网络就是以太网。

3.2、包的收发操作

IP模块生成网络包只是内存中的一串数字,没有办法直接发送给对象,需要转换成电信号或者光信号才能在网线上传输,这才是真正的数据发送过程,而网卡就是负责这一操作的。

网卡的内部结构如图所示,这是一张网卡主要构成要素的概念图,并不代表硬件的实际结构D,但依然可以看清大体的思路。

3.2.1、将包转换成电信号发送到网线中

网卡驱动从IP模块获取包之后,会将其复制到网卡内的缓冲区中,然后向MAC模块发送发送包的命令。

首先,MAC模块会将包从缓冲区中取出,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。如图

图中显示了协议栈和网卡对包的处理过程。MAC头部很容易被误解为是由网卡来处理的,实际上它是由TCP/IP软件来处理的。

  1. 报头是一串像10101010...这样1和0交替出现的比特序列,长度为56比特,它的作用是确定包的读取时机。
  2. 起始帧分界符的末尾比特排列有少许变化。接收方以这一变化作为标记,从这里开始提取网络包数据。也就是说,起始帧分界符是一个用来表示包起始位置的标记。
  3. 末尾的FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误,它是一串32比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的当原始数据中某一个比特发生变化时,计算出来的结果就会发生变化。在包传输过程中,如果受到噪声的干扰而导致其中的数据发生了变化,那么接收方计算出的FCS和发送方计算出的FCS就会不同,这样我们就可以判断出数据有没有错误。
    要深入理解这一块的内容需要先了解如何通过电信号来读取数据,这里我们就不再展开。
3.2.2、向集线器发送网络包

加上报头、起始帧分界符和FCS之后,我们就可以将包通过网线发送出去了。发送的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式。

  • 半双工
    半双工需要判断网络中是否有信号在传输,以避免发生信号碰撞。在确认网络中没有信号传输后,首先MAC模块从报头开始将数字信息按每个比特转换成电信号,然后由MAU的信号收发模块发送出去。在这里,将数字信息转换成电信号的速率就是网络的传输速率,例如每秒将10 Mbit的数字信息转换为电信号发送出去,则速率就是10 Mbit/s。
    接下来,MAU模块会将信号转换为可以在网线上传输的格式,并通过网线发送出去。以太网规格中对不同的网线类型和速率以及其对应的信号格式进行了规定,但MAC模块并不关心这些区别,而是将可转换为任意格式的通用信号发送给PHY(MAU)模块,然后PHY(MAU)模块再将其转换为可在网线上传输的格式。可以认为PHY(MAU)模块的功能就是对MAC模块产生的信号进行格式转换。
  • 全双工
    全双工模式不用担心信号碰撞,允许同时发送和接收消息,其本质上是利用多根双绞线将发送和接收的线路区分开来。这个我们后面的章节会稍微介绍一下。
3.2.3、什么是信号碰撞

PHY(MAU)的职责并不是仅仅是将MAC模块传递过来的信号通过网线发送出去,它还需要监控接收线路中有没有信号进来。在开始发送信号之前,需要先确认没有其他信号进来,这时才能开始发送。如果在信号开始发送到结束发送的这段时间内一直没有其他信号进来,发送操作就成功完成了。以太网不会确认发送的信号对方有没有收到。根据以太网的规格,两台设备之间的网线不能超过100米A,在这个距离内极少会发生错误,万一B发生错误,协议栈的TCP也会负责搞定,因此在发送信号时没有必要检查错误。

在发送信号的过程中,接收线路不应该有信号进来,但情况并不总是尽如人意,有很小的可能性出现多台设备同时进行发送操作的情况。如果有其他设备同时发送信号,这些信号就会通过接收线路传进来。

在使用集线器的半双工模式中,一旦发生这种情况,两组信号就会相互叠加,无法彼此区分出来,这就是所谓的信号碰撞。这种情况下,继续发送信号是没有意义的,因此发送操作会终止。为了通知其他设备当前线路已发生碰撞,还会发送一段时间的阻塞信号C,然后所有的发送操作会全部停止。

等待一段时间之后,网络中的设备会尝试重新发送信号。但如果所有设备的等待时间都相同,那肯定还会发生碰撞,因此必须让等待的时间相互错开。具体来说,等待时间是根据MAC地址生成一个随机数计算出来的。

当网络拥塞时,发生碰撞的可能性就会提高,重试发送的时候可能又会和另外一台设备的发送操作冲突,这时会将等待时间延长一倍,然后再次重试。以此类推,每次发生碰撞就将等待时间延长一倍,最多重试10次,如果还是不行就报告通信错误。

4、接受返回包

网卡将包转换为电信号并发送出去的过程到这里就结束了,既然讲到了以太网的工作方式,那我们不妨继续看看接收网络包时的操作过程。

在使用集线器的半双工模式以太网中,一台设备发送的信号会到达连接在集线器上的所有设备。这意味着无论是不是发给自己的信号都会通过接收线路传进来,因此接收操作的第一步就是不管三七二十一把这些信号全都收进来再说。

信号的开头是报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。这个操作和发送时是相反的,即PHY(MAU)模块先开始工作,然后再轮到MAC模块。首先,PHY(MAU)模块会将信号转换成通用格式并发送给MAC模块,MAC模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,还需要检查FCS。具体来说,就是将从包开头到结尾的所有比特套用到公式中计算出FCS,然后和包末尾的FCS进行对比,正常情况下两者应该是一致的,如果中途受到噪声干扰而导致波形发生紊乱,则两者的值会产生差异,这时这个包就会被当作错误包而被丢弃。

如果FCS校验没有问题,接下来就要看一下MAC头部中接收方MAC地址与网卡在初始化时分配给自己的MAC地址是否一致,以判断这个包是不是发给自己的。我们没必要去接收发给别人的包,因此如果不是自己的包就直接丢弃,如果接收方MAC地址和自己MAC地址一致,则将包放入缓冲区中。到这里,MAC模块的工作就完成了,接下来网卡会通知计算机收到了一个包。

通知计算机的操作会使用一个叫作中断的机制。在网卡执行接收包的操作的过程中,计算机并不是一直监控着网卡的活动,而是去继续执行其他的任务。因此,如果网卡不通知计算机,计算机是不知道包已经收到了这件事的。网卡驱动也是在计算机中运行的一个程序,因此它也不知道包到达的状态。在这种情况下,我们需要一种机制能够打断计算机正在执行的任务,让计算机注意到网卡中发生的事情,这种机制就是中断。

具体来说,中断的工作过程是这样的。首先,网卡向扩展总线中的中断信号线发送信号,该信号线通过计算机中的中断控制器连接到CPU。当产生中断信号时,CPU会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序。然后,中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。

网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包,并通过MAC头部中的以太类型字段判断协议的类型。现在我们在大多数情况下都是使用TCP/IP协议,但除了TCP/IP之外还有很多其他类型的协议,例如NetWare中使用的IPX/SPX,以及Mac电脑中使用的AppleTalk等协议。这些协议都被分配了不同的以太类型,如0080(十六进制)代表IP协议,网卡驱动就会把这样的包交给TCP/IP协议栈;如果是809B则表示AppleTalk协议,就把包交给AppleTalk协议栈,以此类推。

  • 将响应包从IP传递给TCP
    下面我们假设We b服务器返回了一个网络包,那么协议栈会进行哪些处理呢?服务器返回的包的以太类型应该是0800,因此网卡驱动会将其交给TCP/IP协议栈来进行处理。接下来就轮到IP模块先开始工作了,第一步是检查IP头部,确认格式是否正确。如果格式没有问题,下一步就是查看接收方IP地址。如果接收网络包的设备是一台Windows客户端计算机,那么服务器返回的包的接收方IP地址应该与客户端网卡的地址一致,检查确认之后我们就可以接收这个包了。
    如果接收方IP地址不是自己的地址,那一定是发生了什么错误。客户端计算机不负责对包进行转发,因此不应该收到不是发给自己的包。当发生这样的错误时,IP模块会通过ICMP消息将错误告知发送方。
    ICMP规定了各种类型的消息,如图所示。当我们遇到这个错误时,IP模块会通过图中的Destination unreachable消息通知对方。从这张表的内容中我们可以看到在包的接收和转发过程中能够遇到的各种错误,因此希望大家看一看这张表。

如果接收方IP地址正确,则这个包会被接收下来,这时还需要完成另一项工作。IP协议有一个叫作分片的功能。网线和局域网中只能传输小包,因此需要将大的包切分成多个小包。如果接收到的包是经过分片的,那么IP模块会将它们还原成原始的包。分片的包会在IP头部的标志字段中进行标记,当收到分片的包时,IP模块会将其暂存在内部的内存空间中,然后等待IP头部中具有相同ID的包全部到达,这是因为同一个包的所有分片都具有相同的ID。此外,IP头部还有一个分片偏移量(fragment offset)字段,它表示当前分片在整个包中所处的位置。根据这些信息,在所有分片全部收到之后,就可以将它们还原成原始的包,这个操作叫作分片重组。

到这里,IP模块的工作就结束了,接下来包会被交给TCP模块。TCP模块会根据IP头部中的接收方和发送方IP地址,以及TCP头部中的接收方和发送方端口号来查找对应的套接字。找到对应的套接字之后,就可以根据套接字中记录的通信状态,执行相应的操作了。例如,如果包的内容是应用程序数据,则返回确认接收的包,并将数据放入缓冲区,等待应用程序来读取;如果是建立或断开连接的控制包,则返回相应的响应控制包,并告知应用程序建立和断开连接的操作状态。关于为什么查找套接字同时需要接收方和发送方的IP地址和端口号,我们会在后面的章节中介绍。

5、UDP协议的收发操作

5.1、不需要重发的数据用UDP发送更高效

大多数的应用程序都像之前介绍的一样使用TCP协议来收发数据,但当然也有例外。有些应用程序不使用TCP协议,而是使用UDP协议来收发数据。向DNS服务器查询IP地址的时候我们用的也是UDP协议。下面就简单介绍一下UDP协议。

其实TCP中就包含了UDP的一些要点。TCP的工作方式十分复杂,如果我们能够理解TCP为什么要设计得如此复杂,也就能够理解UDP了。那么,为什么要设计得如此复杂呢?因为我们需要将数据高效且可靠地发送给对方。为了实现可靠性,我们就需要确认对方是否收到了我们发送的数据,如果没有还需要再发一遍。

要实现上面的要求,最简单的方法是数据全部发送完毕之后让接收方返回一个接收确认。这样一来,如果没收到直接全部重新发送一遍就好了,根本不用像TCP一样要管理发送和确认的进度。但是,如果漏掉了一个包就要全部重发一遍,怎么看都很低效。为了实现高效的传输,我们要避免重发已经送达的包,而是只重发那些出错的或者未送达的包。TCP之所以复杂,就是因为要实现这一点。

不过,在某种情况下,即便没有TCP这样复杂的机制,我们也能够高效地重发数据,这种情况就是数据很短,用一个包就能装得下。如果只有一个包,就不用考虑哪个包未送达了,因为全部重发也只不过是重发一个包而已,这种情况下我们就不需要TCP这样复杂的机制了。而且,如果不使用TCP,也不需要发送那些用来建立和断开连接的控制包了。此外,我们发送了数据,对方一般都会给出回复,只要将回复的数据当作接收确认就行了,也不需要专门的接收确认包了。

5.2、控制用的端数据

这种情况就适合使用UDP。像DNS查询等交换控制信息的操作基本上都可以在一个包的大小范围内解决,这种场景中就可以用UDP来代替TCP。UDP没有TCP的接收确认、窗口等机制,因此在收发数据之前也不需要交换控制信息,也就是说不需要建立和断开连接的步骤,只要在从应用程序获取的数据前面加上UDP头部,然后交给IP进行发送就可以了。

接收也很简单,只要根据IP头部中的接收方和发送方IP地址,以及UDP头部中的接收方和发送方端口号,找到相应的套接字并将数据交给相应的应用程序就可以了。除此之外,UDP协议没有其他功能了,遇到错误或者丢包也一概不管。因为UDP只负责单纯地发送包而已,并不像TCP一样会对包的送达状态进行监控,所以协议栈也不知道有没有发生错误。但这样并不会引发什么问题,因此出错时就收不到来自对方的回复,应用程序会注意到这个问题,并重新发送一遍数据。这样的操作本身并不复杂,也并不会增加应用程序的负担。UDP头部的控制信息如下所示

5.3、音频和视频数据

还有另一个场景会使用UDP,就是发送音频和视频数据的时候。音频和视频数据必须在规定的时间内送达,一旦送达晚了,就会错过播放时机,导致声音和图像卡顿。如果像TCP一样通过接收确认响应来检查错误并重发,重发的过程需要消耗一定的时间,因此重发的数据很可能已经错过了播放的时机。一旦错过播放时机,重发数据也是没有用的,因为声音和图像已经卡顿了,这是无法挽回的。当然,我们可以用高速线路让重发的数据能够在规定的时间内送达,但这样一来可能要增加几倍的带宽才行。 此外,音频和视频数据中缺少了某些包并不会产生严重的问题,只是会产生一些失真或者卡顿而已,一般都是可以接受的。 在这些无需重发数据,或者是重发了也没什么意义的情况下,使用UDP发送数据的效率会更高。

总结

本章我们探索了在收发数据时,操作系统中的协议栈是如何工作的,以及网卡是如何将包转换成电信号通过网线发送出去的。到这里,我们的网络包已经沿着网线流出了客户端计算机。可以说,网络连接和收发操作最重要也是最难理解的部分已经结束,后续的篇章主要涉及到集线器、交换器、路由器等网络设备的工作原理,接入网的结构,防火墙与代理以及一部分服务器内部的处理方式。这些内容不是本系列的重点,我会在后续的文章中挑一些相对重要的概念来简单说一说。

相关推荐
老六ip加速器2 小时前
手机IP地址更换的影响与方法
网络协议·tcp/ip·智能手机
菜鸟康9 小时前
C++实现分布式网络通信框架RPC(3)--rpc调用端
分布式·网络协议·rpc
别NULL15 小时前
《TCP/IP 详解 卷1:协议》第6章:DHCP和自动配置
网络协议·tcp/ip·php
9527出列15 小时前
网络是如何连接的(二)
网络协议
网硕互联的小客服16 小时前
宝塔面板如何关闭https强制跳转http/https共存?
运维·服务器·网络·网络协议·http·https
weixin_4471952916 小时前
Conda 创建新环境时报错 HTTP 502,如何解决?
网络协议·http·conda
00后程序员张19 小时前
实战解析:如何用克魔(KeyMob)等工具构建iOS应用稳定性与数据可观测体系
websocket·网络协议·tcp/ip·http·网络安全·https·udp
qq_2906062720 小时前
监听本地tcp,udp端口,通过socks5转发至远端
网络协议·tcp/ip·udp
西北大程序猿21 小时前
服务器代码知识点补充
服务器·开发语言·网络·c++·网络协议