Photon服务器引擎(二)socket/TCP/UDP基础及Unity聊天室的实现
我们平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。
通过Socket,我们才能使用TCP/IP协议。实际上,Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。
网络有一段关于socket和TCP/IP协议关系的说法比较容易理解:
“TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。
这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,
TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”
1、TCP协议实现原理
TCP数据包主要包括:
1、SYN包:请求建立连接的数据包
2、ACK包:回应数据包,表示接收到了对方的某个数据包
3、PSH包:正常数据包
4、FIN包:通讯结束包
5、RST包:重置连接
6、URG包:紧急指针
一次完成的TCP通讯包括:建立连接、数据传输、关闭连接
建立连接(三次握手):
1、客户端通过向服务器端发送一个SYN来建立一个主动打开,作为三路握手的一部分。
2、服务器端应当为一个合法的SYN回送一个SYN/ACK。
3、最后,客户端再发送一个ACK。这样就完成了三路握手,并进入了连接建立状态。
数据传输:
1、发送数据端传输PSH数据包
2、接收数据端回复ACK数据包
关闭连接(四次分手):
1、一端主动关闭连接。向另一端发送FIN包。
2、接收到FIN包的另一端回应一个ACK数据包。
3、另一端发送一个FIN包。
4、接收到FIN包的原发送方发送ACK对它进行确认。
下面为使用TCP协议实现一个简单服务器端:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading.Tasks;
-
- class Program {
- static void Main(string[] args) {
-
- Socket tcpServer = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
-
- IPAddress ipaddress = new IPAddress(new byte[]{222,20,30,68});
- EndPoint point = new IPEndPoint(ipaddress,7788);
- tcpServer.Bind(point);
-
- tcpServer.Listen(100);
- Console.WriteLine("开始监听");
-
- Socket clientSocket = tcpServer.Accept();
- Console.WriteLine("一个客户端连接过来了");
-
- string message = "hello 欢迎你";
- byte[] data = Encoding.UTF8.GetBytes(message);
- clientSocket.Send(data);
- Console.WriteLine("向客户端发送了一跳数据");
-
- byte[] data2 = new byte[1024];
- int length = clientSocket.Receive(data2);
- string message2 = Encoding.UTF8.GetString(data2, 0, length);
- Console.WriteLine("接收到了一个从客户端发送过来的消息:"+message2);
-
- Console.ReadKey();
- }
- }
TcpListener的使用:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading.Tasks;
-
- class Program {
- static void Main(string[] args) {
-
- TcpListener listener = new TcpListener(IPAddress.Parse("222.20.30.68"), 7788);
-
-
- listener.Start();
-
-
- TcpClient client = listener.AcceptTcpClient();
-
-
- NetworkStream stream = client.GetStream();
-
- byte[] data = new byte[1024];
-
- while (true)
- {
-
-
- int length = stream.Read(data, 0, 1024);
- string message = Encoding.UTF8.GetString(data, 0, length);
- Console.WriteLine("收到了消息:" + message);
- }
-
-
- stream.Close();
- client.Close();
- listener.Stop();
- Console.ReadKey();
- }
- }
2、UDP协议实现原理
UDP协议在IP协议上增加了复用、分用和差错检测功能。UDP的特点:
1、是无连接的。相比于TCP协议,UDP协议在传送数据前不需要建立连接,当然也就没有释放连接。
2、是尽最大努力交付的。也就是说UDP协议无法保证数据能够准确的交付到目的主机。也不需要对接收到的UDP报文进行确认。
3、是面向报文的。也就是说UDP协议将应用层传输下来的数据封装在一个UDP包中,不进行拆分或合并。因此,运输层在收到对方的UDP包后,会去掉首部后,将数据原封不动的交给应用进程。
4、没有拥塞控制。因此UDP协议的发送速率不送网络的拥塞度影响。
5、UDP支持一对一、一对多、多对一和多对多的交互通信。
6、UDP的头部占用较小,只占用8个字节。
下面为使用UDP协议实现一个简单服务器端:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- class Program
- {
- private static Socket udpServer;
- static void Main(string[] args) {
-
- udpServer = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
-
- udpServer.Bind( new IPEndPoint( IPAddress.Parse("192.168.0.112"),7788 ) );
-
-
- new Thread(ReceiveMessage){ IsBackground = true}.Start();
-
-
- Console.ReadKey();
- }
-
- static void ReceiveMessage()
- {
- while (true)
- {
- EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
- byte[] data = new byte[1024];
- int length = udpServer.ReceiveFrom(data, ref remoteEndPoint);
- string message = Encoding.UTF8.GetString(data, 0, length);
- Console.WriteLine("从ip:" + (remoteEndPoint as IPEndPoint).Address.ToString() + ":" + (remoteEndPoint as IPEndPoint).Port + "收到了数据:" + message);
- }
-
- }
- }
udpClient的使用:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading.Tasks;
-
- class Program {
- static void Main(string[] args) {
-
- UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse("192.168.0.112"),7788));
-
- while (true)
- {
-
- IPEndPoint point = new IPEndPoint(IPAddress.Any, 0);
- byte[] data = udpClient.Receive(ref point);
- string message = Encoding.UTF8.GetString(data);
- Console.WriteLine("收到了消息:" + message);
- }
-
-
- udpClient.Close();
- Console.ReadKey();
- }
- }
3、Unity实现聊天室功能
客户端的实现代码:
- using UnityEngine;
- using System.Collections;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
-
- public class ChatManager : MonoBehaviour
- {
- public string ipaddress = "222.20.30.68";
- public int port = 7788;
- public UIInput textInput;
- public UILabel chatLabel;
-
- private Socket clientSocket;
- private Thread t;
- private byte[] data = new byte[1024];
- private string message = "";
-
- void Start () {
- ConnectToServer();
- }
-
-
- void Update () {
- if (message != null && message != "")
- {
- chatLabel.text += "\n" + message;
- message = "";
- }
- }
-
- void ConnectToServer()
- {
-
- clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
-
- clientSocket.Connect(new IPEndPoint(IPAddress.Parse("222.20.30.68"),7788));
-
- t = new Thread(ReceiveMessage);
- t.Start();
- }
-
-
-
- void ReceiveMessage()
- {
- while (true)
- {
- if (clientSocket.Connected == false)
- break;
-
- int length = clientSocket.Receive(data);
- message = Encoding.UTF8.GetString(data, 0, length);
-
- }
- }
-
- void SendMessage(string message)
- {
- byte[] data = Encoding.UTF8.GetBytes(message);
- clientSocket.Send(data);
- }
-
- public void OnSendButtonClick()
- {
- string value = textInput.value;
- SendMessage(value);
- textInput.value = "";
- }
-
- void OnDestroy()
- {
- clientSocket.Shutdown(SocketShutdown.Both);
-
- clientSocket.Close();
- }
- }
服务器端的实现代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
-
-
- namespace _022_聊天室_socket_tcp服务器端 {
-
-
-
- class Client
- {
- public string name=null;
- private Socket clientSocket;
- private Thread t;
- private byte[] data = new byte[1024];
-
- public Client(Socket s,string i)
- {
- clientSocket = s;
- name = i;
-
- t = new Thread(ReceiveMessage);
- t.Start();
- }
-
- private void ReceiveMessage()
- {
-
- while (true)
- {
-
- if (clientSocket.Poll(10, SelectMode.SelectRead))
- {
- clientSocket.Close();
- break;
- }
-
- int length = clientSocket.Receive(data);
- string message = Encoding.UTF8.GetString(data, 0, length);
-
-
- Program.BroadcastMessage(message,name);
- Console.WriteLine("收到 "+name +" 的消息:" + message);
- }
- }
-
- public void SendMessage(string message)
- {
- byte[] data = Encoding.UTF8.GetBytes(message);
- clientSocket.Send(data);
- }
-
- public bool Connected
- {
- get { return clientSocket.Connected; }
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Net.WebSockets;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace _022_聊天室_socket_tcp服务器端 {
- class Program {
- static List<Client> clientList = new List<Client>();
-
-
-
-
-
- public static void BroadcastMessage(string message,string name)
- {
- var notConnectedList = new List<Client>();
- foreach (var client in clientList)
- {
- if (client.Connected)
- client.SendMessage(name+" : "+message);
- else
- {
- notConnectedList.Add(client);
- }
- }
- foreach (var temp in notConnectedList)
- {
- clientList.Remove(temp);
- }
- }
- static void Main(string[] args) {
- int i = 0;
- string name = null;
- Socket tcpServer = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
- tcpServer.Bind(new IPEndPoint(IPAddress.Parse("222.20.30.68"),7788));
-
- tcpServer.Listen(100);
- Console.WriteLine("server running...");
-
- while (true)
- {
- i = i + 1;
- if (i == 1) { name = "Tom"; }
- else if (i == 2) { name = "Alice";}
- else { name = "Other"; }
- Socket clientSocket = tcpServer.Accept();
- Console.WriteLine("用户 "+name+" 已连接 !");
- Client client = new Client(clientSocket,name);
- clientList.Add(client);
- }
-
- }
- }
- }
打开两个客户端。记得代码中的IP地址要改成自己的PC机的IP地址。
实现效果:
===================================================================================
结束。
祝大家国庆节快乐!
下一节直接上手Photon服务器引擎吧。