[Unity] U3D网络开发实战(0)

第一章:网络游戏的开端:Echo

藏在幕后的服务端

网络连接的端点:Socket

Socket

网络上的两个程序通过一个双向的通信连接实现数据交换,这个连接的一段称为一个Socket。一个Socket包含了进行网络通信必需的五种信息:连接使用的协议、本地主机的IP地址、本地的协议端口、远程主机的IP地址和远程协议端口。

IP地址

网络上的计算机都是通过IP地址识别的,应用程序通过通信端口彼此通信。

端口

端口是个逻辑概念,随着多任务系统的发展,人们定义了”端口”的概念,把不同的网络消息分发给不同的任务。

每一个进程可以用哟多个Socket,每个Socket通过不同端口与其他计算机连接。每一条Socket连接代表着本地Socket——本地端口——网络介质——远程端口——远程Socket的链路

和打电话一样,Socket通信也分为”连接方”和”监听方”,连接方使用不同的端口连接,监听方只使用一个端口监听。

Socket通信的流程

  1. 开启一个连接之前,需要创建一个Socket对象(使用API Socket),然后绑定本地对应的端口(使用API Bind)。对服务端而言,绑定的步骤相当于给手机插上SIM卡,确定了”手机号”。对客户端而言,连接时(使用API Connect)会由系统分配端口,可以省去绑定步骤。

  2. 服务端开启监听(使用API Listen),等待客户端接入。相当于电话开机,等待别人呼叫。

  3. 客户端连接服务器(使用API Connect),相当于手机拨号。

  4. 服务器接受连接(使用API Accept),相当于接听电话并说出”喂”。

    通过这四个步骤,成功建立连接,可以收发数据。

  5. 客户端和服务端通过Send和Receive等API收发数据,操作系统会自动完成数据的确认、重传等步骤,确保传输的数据准确无误。

  6. 某一方关闭连接(使用API Close),操作系统会执行”四次挥手”的步骤,关闭双方连接,相当于挂断电话。

【图】

TCP 和 UDP协议

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,与TCP相对应的UDP协议是无连接的、不可靠的、但传输效率较高的协议。在本系列的语义中,”Socket通信”特指TCP协议的Socket通信。

开始网络编程:Echo

什么是Echo程序

Echo程序是网络编程中最基础的案例。建立网络连接后,客户端向服务端发送一行文本,服务端收到后将文本发送回客户端。

编写客户端程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using UnityEngine.UI;


public class Chap1_Echo : MonoBehaviour {

// 定义套接字
Socket socket;
// UGUI
public InputField inputField;
public Text text;

// 点击连接按钮
public void Connection()
{
// Socket
socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
// Connect
socket.Connect("127.0.0.1",8888);
}
// 点击发送按钮
public void Send()
{
// Send
string sendStr = inputField.text;
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
socket.Send(sendBytes);

// Reveice
byte[] readBuff = new byte[1024];
int count = socket.Receive(readBuff);
string recvStr = System.Text.Encoding.Default.GetString(readBuff,0,count);

text.text = recvStr;

// Close
socket.Close();
}

}

客户端代码知识点

  1. using System.Net.Sockets;Socket编程的API位于这个命名空间下。

  2. 创建Socket对象:Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)

    • 地址族:指明使用IPv4还是IPv6
    • SocketType:套接字类型,类型参考SocketType Enum
    • ProtocolType:指明协议,本例使用的是TCP协议,部分协议类型参考ProtocolType Enum
  3. 连接Connect

    客户端通过socket.Connect(远程IP地址,远程端口)连接服务端。Connect是一个阻塞方法,程序会卡住直到服务端回应(接受、拒绝或超时)。

  4. 发送信息Send

    客户端通过socket.Send发送数据,这也是一个阻塞方法。该方法接受一个byte[]类型的参数指明要发送的内容。Send的返回值指明发送数据的长度。

  5. 接收消息Receive

    客户端通过socket.Receive接收服务端数据,这也是一个阻塞方法,没有收到服务端数据时,程序将卡在Receive不会向下执行。该方法接受一个byte[]类型的参数,存储接收到的数据,Receive的返回值指明接收数据的长度。

  6. 关闭连接Close

编写服务端程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
using System.Net;
using System.Net.Sockets;
namespace EchoSever
{
class MainClass
{
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);
Console.WriteLine("[服务器]启动成功");
while(true)
{
// Accept
Socket connfd = listenfd.Accept();
Console.WriteLine("[服务器]Accept");
// Receive
byte[] readBuff = new byte[1024];
int count = connfd.Receive(readBuff);
string readStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Console.WriteLine("[服务器接收]" + readStr);
// Send
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(readStr);
connfd.Send(sendBytes);
}

}
}
}

服务端知识点:

  1. 绑定Bind

    listenfd.Bind(ipEp)将给listenfd套接字绑定IP和端口,程序中绑定本地地址”127.0.0.1”和8888号端口。127.0.0.1是回送地址,指本地机,一般用于测试。

  2. 监听Listen

    服务端通过listenfd.Listen(backlog)开启监听,等待客户端连接。参数backlog指定队列中最多可容纳等待接受的连接数,0表示不限制。

  3. 应答Accept

    开启监听之后,服务器调用listenfd.Accept()接收客户端连接。本例使用的所有Socket方法都是阻塞方法,也就是说当没有客户端连接的时候,服务端程序会卡在listenfd.Accept()不会向下执行,直到收到了客户端的连接。Accept返回一个新客户端的Socket对象,对于服务器来说,它有一个监听Socket(listenfd)用来监听和应答客户端的连接,对每个客户端还有一个专门的Socket(connfd)用来处理客户端的数据。

  4. IPAddress和IPEndPoint

    用IPAddress指定IP地址,用IPEndPoint指定IP和端口。

  5. System.Text.Encoding.Default.GetString

    Receive方法将接收到的字节流保存在readBuff上,readBuff是byte型数组。

公网和局域网