Unity3D网络游戏实战|01基础
Socket
网络游戏分为客户端与服务端,客户端与服务端之间通过网络协议进行交流,比如TCP、UDP、KCP、HTTP等
此时,每一端都被称为一个Socket 一个Socket包含网络通讯的五种必要信息:协议、本地IP、本地端口、远程IP、远程端口 Socket = 协议 + IP + 端口
TCP通讯流程
先说明一些概念
- sequence number: 序列号
- acknowledgement number:确认号
- SYNchronization:同步标志
- ACKnowlegment:确认标志
- FINish:终止标志
- seq 32位,用来标识从A发送到B的数据包的序号,计算机发送数据时对此进行标记。
- ack 32位,客户端和服务器端都可以发送,ack = seq + 1。
- 标志位 每个标志位占用1位,共有6个,分别为 URG、ACK、PSH、RST、SYN、FIN
- ACK 确认序号有效
- SYN 建立一个新连接
- FIN 断开一个连接
三次握手
建立连接需要进行3次沟通
第一次握手 Client向Server发送连接请求,标志位为SYN,并带有一个随机seq假设为1000
第二次握手 Server需要干两件事情
- 首先向Client发送确认请求,表示收到连接,标志位为ACK,ack为1000+1=1001,此时说明Client可以向Server发送信息了
- 但是Server也需要向Client发送信息,所以Server一样需要一个发送一个连接请求,标志位为SYN,seq为一个随机数假设为2000
第三次握手 这次是Client确认Server的请求,标志位为ACK,ack为2000+1=2001
四次挥手
第一次挥手 Client向Server发送关闭请求,标志位为FIN,seq为随机数,假设为5000
第二次挥手 Server想Client发送确认信息,标志位为ACK,ack为5000+1=5001,此时Client向Sever的连接可以断开
第三次挥手 Server向Client发送关闭请求,标志位为FIN,seq为随机数,假设为7000
第四次挥手 Clent向Server发送确认信息,标志位为ACK,ack=7000+1=7001
基础通讯模型
客户端
新建项目NetStudy,编写如下代码,UGUI部分省略了
public class Echo : MonoBehaviour
{
public InputField InputFeld;
public Button connect;
public Button send;
private Socket socket;
private void Start()
{
connect.onClick.AddListener(Connection);
send.onClick.AddListener(Send);
}
private void Connection()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("127.0.0.1", 8888);
}
private void Send()
{
//Send
string sendStr = InputFeld.text;
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
socket.Send(sendBytes);
//Recv
byte[] readBuff = new byte[1024];
int count = socket.Receive(readBuff);
string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Debug.Log(recvStr);
socket.Close();
}
}
new Socket ( AddressFamily , SocketType , ProtocolType )
- Enum: AddressFamily 地址类型
- InterNetwork 代表使用IPV4
- InterNetworkV6 代表使用IPV6
- Enum: SocketType 数据流套接字类型
- Enum: ProtocolType 协议类型
Socket.Connect ( Host , Port ) : void 此方法是阻塞方法,再未连接到地址之前,都会阻塞进程
- String: Host IP地址
- Int: Port 端口号
Socket.Send(Bytes) : int 此方法为阻塞方法,向服务器发送数据
- Byte[]: Bytes 发送的字节数据
- Return 返回发送数据的长度
Socket.Receive(Bytes) : int 此方法为阻塞方法
- Bytes[]: Bytes 用于接收数据的字节数组
- Return 返回接收到数据的长度
Socket.Close() 关闭连接
此时运行项目必然是报错的,因为我们压根就没有服务端程序…
服务端
新建项目NetServerStudy并编写如下代码
public class Server : MonoBehaviour
{
private Socket server;
private IPAddress ipAdr;
private IPEndPoint ipEp;
private void Start()
{
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ipAdr = IPAddress.Parse("127.0.0.1");
ipEp = new IPEndPoint(ipAdr, 8888);
server.Bind(ipEp);
server.Listen(0);
//Accept
var connect = server.Accept();
Debug.Log("[服务器]Accept");
//Receive
byte[] readBuff = new byte[1024];
int count = connect.Receive(readBuff);
string readStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Debug.Log("[服务器接收]" + readStr);
//Send
byte[] sendBytes = System.Text.Encoding.Default.GetBytes($"服务器返回数据:{readStr}");
connect.Send(sendBytes);
}
}
IPAddress IP地址
IPEndPoint IP地址+端口
Socket.Bind ( IPEndPoint )
- IPEndPoint: IPEndPoint 绑定地址,IP+端口号
Socket.Listen(BackLog) 开启监听(可以接受客户端信号)
- Int: BackLog 最大可接受数量,0代表无限
Socket.Accept () : Socket 等待接受客户端连接,是一个阻塞方法,如果没有客户端连接,那么会一直堵塞线程
- Return 代表服务器访问客户端的Socket
现在可以测试一下了,首先运行服务端,会发现卡住了,很正常,因为Accept是一个阻塞方法
然后运行客户端
首先点击Connect,然后Send
客户端结果:
服务端结果: