<audio id="audio" title="第18讲 | 如何判断客户端的网络连接？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/21/a4/213efd8525d38e60060de7d47c4d5ea4.mp3"></audio>

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

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

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

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

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

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

我来分别解释一下。

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

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

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

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

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

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

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

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

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

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

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

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

在Python中编写TCP协议的代码比之使用C/C++更为方便。因为C/C++需要初始化一系列的内容，然后进行顺序的流程化绑定，设置网络参数，最后进行发送和接收操作，在结束的时候进行资源的回收。而在Python这里，只需要设置协议和IP地址就可以实现TCP协议编程。我们来看一段代码。

```
import socket
class go_sock(object):
    ip = &quot;&quot;
    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(&quot;115.231.74.62&quot;, 21)
_inet.sock.recv(100)

```

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

## 小结

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

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

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

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

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