第18讲 如何判断客户端的网络连接? 由于涉及到网络、脚本语言等等,这一节起,我要开始讲一些基础的、看起来比较枯燥的知识。我会尽力写得有趣生动,但是,知识的获取并不容易,即便我已经在努力去讲解,还是需要你用更多的时间去摸索和学习。

我们在前面说了Pygame的一些客户端知识,如果你想让这款游戏能够在网络上传输数据,接下来,那就需要编写服务器端,并且在客户端做客户端连接的准备。

前面我们已经用Pygame讲解了很多内容,那我们客户端的网络连接,我也继续使用Python来编写,当然这已经和Pygame没有关系了。因为网络连接是独立的代码,所以只需要直接写在游戏里就可以了。

在开始编写网络部分之前,我们需要整理一下网络的基础知识。如果我们一上来就编写网络代码,对于你来说,可能会有一些概念上的模糊。

对于网络编程,或许你已经接触到了,或许你只是有点概念,或许你一点都没接触过。但是你或许听说过Socket套接字、TCP/IP协议、UDP协议、HTTP、HTTPS协议等等,那么这些协议是不是都属于网络编程范畴?还是这里面哪些部分是属于网络编程范畴呢?

网络,从模型上讲,一共分为七层,从底层到最上层,分别是:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

我来分别解释一下。

  • 物理层:所谓的物理层,你可以理解为我们看到的各种网络线,也就是人肉眼能看到的物理线路,包括光纤、以前连接调制解调器的电话线等等。这些线路就是物理层。物理层有物理层的规范,比如电流、编码、帧、连接头等等。你只需要知道物理层也是存在规范的,就可以了。物理层最主要的功能就是网络的物理连接
  • 数据链路层:所谓的数据链路层,就是建立逻辑连接,然后进行硬件上的寻址操作、差错的校验,然后将二进制的字节进行组合,通过MAC地址进行访问,比如网卡、交换机等等。你需要记住的是,在这一层,要通过MAC地址来进行访问,进行硬件寻址操作。
  • 网络层:网络层进行逻辑地址的寻址操作和数据链路层不同。数据链路是使用硬件寻址操作,而网络层是使用逻辑地址寻址,比如路由器、防火墙、多层交换机等等。我们最熟悉的IPv4(202.101.253.233)、IPv6、ARP等等都属于这一层。你在这里需要记住的是,网络层是逻辑寻址操作,会用到ARP、IPv4等等协议。
  • 传输层:在编程中最常用到的TCP、UDP等等协议,都在这一层进行操作,它首先定义了数据传输的协议端口号以及一些错误的检测。
  • 会话层:会话层在传输层之上,它就在客户端和服务器端。严谨地说,就是本地机器和远端机器之间的会话,比如要进行断点续传这些操作,就属于会话层的范畴。
  • 表示层:表示层很容易理解,就是数据的传输,然后展现在电脑上。比如图片的传输和显示、网络地址的加密等等。
  • 应用层:应用层就是提供给电脑用户的各种网络应用,比如你自己编写的网络软件、FTP软件、Telnet、浏览器等等。

以上这些点你要硬性记住的话,会比较困难。我教给你一个方法。

首先,我们想象一段从网线过来一段数据,网线就是“物理层”,那么数据需要找到一个门牌号,这个门牌号是一个硬件地址,可能是你的电脑网卡,也可能是你公司的交换机。这些数据需要把这些门牌地址连接起来,这就是“数据链路层”。

随后,这些数据找到门牌号后,就需要分发到逻辑地址,比如路由器或者你的IP地址,这些逻辑地址就是网络地址,这就是“网络层”。

经过网络层后,就要看这是什么数据,是TCP协议的,还是UDP协议的。知道了协议后,才可以传输数据,所以这个是“传输层”。

那么在传输的过程中,可能会中断,所以我们需要登录服务器,断点续传进行重新传输,这些属于机器和机器之间的会话,所以是“会话层”。

传输完数据后,我们就会在电脑里显示这个内容,是一幅图片呢,还是一段电影?这个需要表示出来,所以是“表示层”。

最后,我们将这个一整套的东西,写成了一个应用,这就是“应用层”。

虽然这么表述起来,有许多不精确不严谨的地方,但是通过这段话能让你很快记住这个七层网络模型,对你将来的编程有很大的帮助。

Python支持Socket编程,也就是支持TCP、UDP等协议的编程,也支持更上层的编程,比如HTTP协议等等。在今天的内容中,我们要用到TCP编程。至于为什么要使用TCP,有这样几个原因:

  • TCP保证连接的正确性。在建立TCP连接的时候,需要经过三次握手,连接这一方发送SYN协议,被连接方返回SYN+ACK协议,最后连接方再返回ACK协议;
  • TCP保证如果在一定时间内没有收到对方的信息,就重发消息,保证消息传输的可靠性
  • TCP可以进行流量控制。它拥有固定大小的缓冲池,TCP的接收方只允许另一方发送缓冲池内所接纳的数据大小。

TCP还有其他更多的保证传输可靠性的内容和标准,我在这里不做更多的阐述。另外,使用TCP可以进行长时间的连接,在客户端和服务器端之间进行不停地交互。在交互过程中,服务器端发送数据给客户端,客户端就能做出相应的回应。

在Python中编写TCP协议的代码比之使用C/C++更为方便。因为C/C++需要初始化一系列的内容,然后进行顺序的流程化绑定,设置网络参数,最后进行发送和接收操作,在结束的时候进行资源的回收。而在Python这里,只需要设置协议和IP地址就可以实现TCP协议编程。我们来看一段代码。 import socket class go_sock(object): ip = “” port = 0 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def init(self): object.init(self) def connect(self, ip, port): self.ip = ip self.port = port self.sock.connect_ex((ip, port)) def close(self): self.sock.close()

我在这里编写了一个类,这个类将TCP的内容封装在了类中,这样,我们的网络代码能在游戏中方便地初始化,使用起来就很方便。

首先,我们看到在类里面定义了ip、port、sock这三个变量,这三个变量分别是对应IP地址、端口号以及socket句柄。在类里,我们直接将sock初始化为socket类,其中socket类填写的内容中,参数1是服务器之间的网络通信,参数2是流Socket,这里指的是TCP协议。

在初始化完成了之后,我们看到connect函数。在函数里面,我们看到参数对变量的初始化,其中sock句柄调用了标准socket函数sock.connect_ex,这个函数负责与对方进行一个连接。

最后的函数是close关闭操作,在任务完成之后,你可以调用close函数进行socket句柄的关闭。

我们可以这样使用这个类。 _inet = go_sock() _inet.connect(“115.231.74.62”, 21) _inet.sock.recv(100)

在这里,我们可以简单测试一下某些应用服务器,然后接收返回内容。这个类的封装工作到此就告一个段落,更多的网络服务和交互的编写,我将在下一节阐述。

小结

今天我们学习了网络的七层模型结构,以及我们将要在游戏中使用的TCP协议的编程。

  • 我用了一个传输过程介绍了七层每一层做的事情,这个你一定要牢记。
  • 我们使用Python封装了Socket库的细节内容,只需要直接编写connect代码就可以进行数据的接收和发送操作了。
  • 选择TCP协议是因为它安全可靠,能保证游戏传输的过程中不出错。

现在,我给你留一个小问题吧。

如果我们要使用UDP来编写这个网络服务,该如何保证数据的准确性呢?选择UDP协议的优势在哪里?

欢迎留言说出你的看法。我在下一节的挑战中等你!

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/%e4%bb%8e0%e5%bc%80%e5%a7%8b%e5%ad%a6%e6%b8%b8%e6%88%8f%e5%bc%80%e5%8f%91/%e7%ac%ac18%e8%ae%b2%20%e5%a6%82%e4%bd%95%e5%88%a4%e6%96%ad%e5%ae%a2%e6%88%b7%e7%ab%af%e7%9a%84%e7%bd%91%e7%bb%9c%e8%bf%9e%e6%8e%a5%ef%bc%9f.md