分为客户端和服务端两个部分,客户端程序运行在用户的电脑或手机上,服务端程序运行在游戏运营商的服务器上。
以下是一些典型的游戏客户端。
《金铲铲之战》客户端
TCP是游戏中常用的网络通信协议,除此之外还有UDP、KCP、HTTP等协议。
玩家1移动->客户端1向服务端发送新的坐标信息->服务端处理信息->服务端将玩家1的新坐标转发给客户端2->客户端2收到信息并更新玩家1的位置。
为了支撑可能百万的用户同时在线,游戏服务端通常采用分布式架构。使用分区的服务端,每个服务端负责不同区的玩家。
客户端连接服务端。服务端之间互相连接(通常使用TCP网络通信),形成服务端集群。
网络中两个程序通过一个双向的通信连接实现数据交换,这个连接的一端称为一个Socket。
网络上的计算机都是通过IP地址识别的,应用程序通过通信端口彼此通信。
端口是设备与外界通信交流的出口。
Socket通信的基本流程如下所示:
(1)开启一个连接前需要创建一个Socket对象(使用API Socket),然后绑定本地使用的端口(API Bind)。客户端在连接时(使用API Connect)会有系统分配端口。(即客户端和服务端都需要先创建Socket然后绑定端口)
(2)服务端开启监听(使用API Listen),等待客户端接入(API Connect)。
(3)客户端连接服务端(使用API Connect)。
(4)服务器接受连接(使用API Accept)。
(5)客户端和服务端通过Send和Receive等API 收发数据,操作系统会自动完成数据的确认、重传等步骤,确保传输的数据准确无误。
(6)某一方关闭连接(API Close),操作系统会执行 ”四次挥手“ 的步骤,关闭双方连接。
核心代码:
public class Echo : MonoBehaviour
{
Socket socket;
public InputField = InputFeld;
public Text text;
public void Connection()
{
//创建Socket
Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//连接服务器
socket.Connect("127.0.0.1",8888);
}
public void Send()
{
string sendStr = Input.Feld.text;
//字符串转换为字节流
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
//发送字节流
socket.Send(sendBytes);
byte[] readBuff = new byte[1024];
//接受消息转换字节流为字符串
int count = socket.Receive(readBuff);
string recvStr = System.Text.Encoding.Default.GetString(readBuff,0,count);
text.text = recvStr;
socket.Close();
}
}
上述程序包含知识点的含义如下:
(1)using System.Net.Sockets
Socket编程的API(如Socket、AddressFamily等)位于System.Net.Sockets命名空间中,因此引用。
(2)创建 Socket 对象
Socket(地址族,套接字类型,协议)用于创建Socket对象。
地址族指明使用IPv4还是IPv6,其值与对应含义如下所示:
AddressFamily的值 | 含义 |
---|---|
InterNetwork | 使用IPv4 |
InterNetworkV6 | 使用IPv6 |
SocketType是指套接字类型(游戏中最常用的是字节流套接字即Stream):
SocketType的值 | 含义 |
---|---|
Dgram | 支持数据报 |
Raw | 支持对基础传输协议的访问 |
RDM | 支持无连接、面向消息、以可靠方式发送的信息 |
Seqpacket | 在网络上提供排序字节流的面向连接且可靠的双向传输 |
Stream | 支持可靠、双向、基于连接的字节流,而且不重复数据,也不保留边界。 |
Unknown | 指定未知的Socket类型 |
微软官方文档
ProtocolType指明协议:
微软官方文档
(3)连接Connect
客户端会通过socket.Connect(远程IP地址,远程端口)连接服务端。Connect是一个阻塞的方法,直到服务端回应(接受、拒绝或超时)。
(4)发送消息Send
通过Socket.Send()方法发送数据,Send是一个阻塞的方法,接受byte[]类型参数为发送内容,返回值为发送数据的长度。使用System.Text.Encoding.Default. GetBytes(sendStr)可将字符串sendStr转换为byte[] 数组。
备注:发送的成功完成并不表示数据已成功传递。 如果传输系统中没有可用于保存要传输的数据的缓冲区空间,除非套接字处于非阻止模式,否则发送将阻塞。
官方文档阻塞描述
(5)接受消息Receive
客户端通过socket.Receive()方法接受服务端消息,Receive是一个阻塞方法,直到接受到服务端数据为止。其返回值为接受到的字符数组的长度。使用System.Text.Encoding.Default.GetString(readBuff,0,count)将字符数组转换为字符串。
(6)关闭链接Close
通过socket.Close关闭连接。
备注:关于详细unity的UI界面创建以及事件的添加本文不做阐述,详情见《Unity3D网络游戏实战》,本文中部分内容引用于其。
class MainClas
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//创建Socket
Socket listenfd = new Socket(AddressFamily.InterNetWork,SocketType.Stream,ProtocolType.Tcp);
//创建Bind
IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEp = new IPEndPoint(ipAdr,8888);
listenfd.Bind(ipEp);
//监听listen
listenfd.Listen(0);
while(true)
{
//等待客户端连接
Socket connfd = listenfd.Accept();
byte[] readBuff = new byte[1024];
//接受信息
int count = connfd.Receive(readBuff);
string readStr = System.Text.Encoding.Default.GetString(readBuff,0,count);
Console.WriteLine("[服务器接受]"+readStr);
//发送信息
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(readStr);
connfd.Send(sendBytes);
}
}
}
上述程序包含知识点的含义如下:
(1)绑定Bind
listenfd.Bind(ipEp)方法将给listenfd套接字绑定IP和端口。
注:“127.0.0.1”是回送地址,指本地机,一般用于测试。
(2)监听Listen
服务器通过listenfd.Listen(backlog)开启监听,等待客户端连接。参数backlog指定队列中最多可容纳等待接受的连接数,0表示不受限制。
(3)应答Accept
开启监听后,服务器调用listenfd.Accept()接收客户端连接,Accept()方法为阻塞方法,直到监听到客户端连接,Accept()方法会返回一个新客户端的Socket对象。
注:对于服务端而言,有一个监听Socket(例中listenfd)用来监听(Listen)和应答(Accept)客户端的连接,对每一个客户端还有一个专门的Socket(例中connfd)用来处理该客户端的数据。
(4)IPAddress和IPEndPoint
使用IPAddress指定IP地址,使用IPEndPoint指定IP和端口。
思考:当前服务端每次只能处理一个客户端的请求,如果我们要做一套聊天系统,它必须同时处理多个客户端的请求,那么该如何实现呢?
System.Net.Socket命名空间中的Socket类为网络通信提供了一套丰富的方法和属性,更多API可查阅微软.Net Socket官方文档。
将服务端连接到公网,例如连接宽带,或者购买阿里云、腾讯云服务器,就可以获得这一台计算机的公网IP。客户端只需要连接这个公网IP和端口,即可连接到服务器。
如果使用无限路由或者局域网,将宽带连接路由器上,再由路由器分发到多台计算机(校园网,公司局域网)。这种情况下,路由器会有公网和局域网两个IP。如果将服务器部署到连接路由器的某台计算机上,因为它只有局域网IP,所以只有局域网内的计算机可以连接上。如果拥有路由器的控制权,可以利用“端口映射”技术使外网计算机访问内网服务端计算机。
如果没有路由器控制权,将服务端程序部署到阿里云、腾讯云等云服务器即可。