Unity C# 网络学习(二)
一.Socket的重要API
public class Lesson05 : MonoBehaviour
{
private void Start()
{
var tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
Debug.Log(tcpSocket.Connected);
Debug.Log(tcpSocket.SocketType);
Debug.Log(tcpSocket.ProtocolType);
Debug.Log(tcpSocket.AddressFamily);
Debug.Log(tcpSocket.Available);
Debug.Log(tcpSocket.LocalEndPoint);
Debug.Log(tcpSocket.RemoteEndPoint);
tcpSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
tcpSocket.Listen(10);
tcpSocket.Accept();
tcpSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
tcpSocket.Send(new byte[10]);
tcpSocket.Shutdown(SocketShutdown.Both);
tcpSocket.Close();
}
}
二.实现一个最简单的服务端
namespace TcpServer
{
class Program
{
static void Main(string[] args)
{
var serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
}
catch (Exception e)
{
Console.WriteLine("绑定失败!" + e.Message);
return;
}
serverSocket.Listen(10);
Console.WriteLine("等待玩家接入!");
var clientSocket = serverSocket.Accept();
Console.WriteLine("有客户端接入了!!!");
clientSocket.Send(Encoding.UTF8.GetBytes("欢迎接入服务器!"));
var buffer = new byte[1024];
var num = clientSocket.Receive(buffer);
Console.WriteLine($"接收到了{0}的消息:{1}", clientSocket.RemoteEndPoint, Encoding.UTF8.GetString(buffer, 0, num));
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
Console.WriteLine("按任意键退出");
Console.ReadKey();
}
}
}
三.实现一个最简单的客户端
public class Lesson06 : MonoBehaviour
{
private void Start()
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
}
catch (Exception e)
{
Debug.Log(e);
return;
}
var buffer = new byte[1024];
var num = socket.Receive(buffer);
Debug.Log("收到服务器的消息:" + Encoding.UTF8.GetString(buffer, 0, num));
socket.Send(Encoding.UTF8.GetBytes("你好,我是客户端"));
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
四.优化服务端
- 由于之前的服务端只能够接收一次客户端发来的消息,并且只能发送一次消息,这样并不能在实际开发中使用,所以要为服务端进行优化
- 用多线程处理客户端连接,接收消息,处理消息
namespace TcpServerPlus
{
class Program
{
private static Socket _serverSocket;
private static List<Socket> _allClientSocketList = new List<Socket>();
static void Main(string[] args)
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
_serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
_serverSocket.Listen(10);
Thread acceptThread = new Thread(AcceptClientConnect);
acceptThread.Start();
Thread reciveThread = new Thread(ReciveClientMsg);
reciveThread.Start();
while (true)
{
var inputStr = Console.ReadLine();
if (inputStr == "Quit")
{
for (int i = 0; i < _allClientSocketList.Count; i++)
{
_allClientSocketList[i].Shutdown(SocketShutdown.Both);
_allClientSocketList[i].Close();
}
break;
}
else if (inputStr.StartsWith("S:", StringComparison.OrdinalIgnoreCase))
{
for (int i = 0; i < _allClientSocketList.Count; i++)
{
_allClientSocketList[i].Send(Encoding.UTF8.GetBytes(inputStr.Substring(2)));
}
}
}
}
static void AcceptClientConnect()
{
while (true)
{
var clientSocket = _serverSocket.Accept();
_allClientSocketList.Add(clientSocket);
clientSocket.Send(Encoding.UTF8.GetBytes("欢迎连接服务器!"));
}
}
static void ReciveClientMsg()
{
Socket clientSocket;
byte[] buffer = new byte[1024 * 1024];
int num = 0;
while (true)
{
for (int i = 0; i < _allClientSocketList.Count; i++)
{
clientSocket = _allClientSocketList[i];
if (clientSocket.Available > 0)
{
num = clientSocket.Receive(buffer);
ThreadPool.QueueUserWorkItem(HandleMsg, (clientSocket, Encoding.UTF8.GetString(buffer, 0, num)));
}
}
}
}
static void HandleMsg(object state)
{
(Socket s, string str) info = ((Socket s, string str))state;
Console.WriteLine($"收到客户端{info.s.RemoteEndPoint}发来的消息:{info.str}");
}
}
}
五.继续优化服务器
- 虽然上面的代码以及可以实现功能了,但是不够面向对象,可以进一步优化
namespace TcpServerPlusPlus
{
class ClientSocket
{
public int id;
public static int BEGIN_ID = 1;
public Socket socket;
public ClientSocket(Socket socket)
{
this.socket = socket;
id = BEGIN_ID++;
}
public void SendMsg(string str)
{
try
{
socket.Send(Encoding.UTF8.GetBytes(str));
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
}
public void ReceiveMsg()
{
if (socket == null)
return;
try
{
if (socket.Available > 0)
{
var buffer = new byte[1024 * 5];
var num = socket.Receive(buffer);
ThreadPool.QueueUserWorkItem(MsgHandle,Encoding.UTF8.GetString(buffer,0,num));
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public void Close()
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
private void MsgHandle(object state)
{
var info = state as string;
Console.WriteLine($"收到客户端{socket.RemoteEndPoint}发来的消息:{info}");
}
}
}
namespace TcpServerPlusPlus
{
class ServerSocket
{
public Socket socket;
private Dictionary<int, ClientSocket> _allID2ClientDic = new Dictionary<int, ClientSocket>();
public void Connent(string ip,int host,int maxNum)
{
if (socket != null)
return;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Bind(new IPEndPoint(IPAddress.Parse(ip), host));
socket.Listen(maxNum);
ThreadPool.QueueUserWorkItem(Accept);
ThreadPool.QueueUserWorkItem(Receive);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public void Broadcast(string info)
{
foreach (var clientSocket in _allID2ClientDic.Values)
clientSocket.SendMsg(info);
}
public void Close()
{
foreach (var clientSocket in _allID2ClientDic.Values)
clientSocket.Close();
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
private void Receive(object state)
{
while (true)
{
try
{
if (_allID2ClientDic.Count > 0)
{
foreach (var clientSocket in _allID2ClientDic.Values)
clientSocket.ReceiveMsg();
}
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
}
}
private void Accept(object state)
{
while (true)
{
try
{
var clientSocket = socket.Accept();
var newClientSocket = new ClientSocket(clientSocket);
_allID2ClientDic.Add(newClientSocket.id,newClientSocket);
newClientSocket.SendMsg("连接成功!");
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
}
namespace TcpServerPlusPlus
{
class Program
{
static void Main(string[] args)
{
var serverSocket = new ServerSocket();
serverSocket.Connent("127.0.0.1", 8080, 10);
Console.WriteLine("服务器开启成功!!!");
while (true)
{
var inputStr = Console.ReadLine();
if (inputStr == "Quit")
{
serverSocket.Close();
break;
}
else if (inputStr.StartsWith("S:", StringComparison.OrdinalIgnoreCase))
{
serverSocket.Broadcast(inputStr.Substring(2));
}
}
}
}
}
六.编写客户端
public class NetMgr : MonoBehaviour
{
private static NetMgr _instance;
public static NetMgr Instance => _instance;
private Socket _socket;
private readonly Queue<string> _sendMsgQueue = new Queue<string>();
private static readonly object SendMsgQueueLock = new object();
private readonly Queue<string> _receiveMsgQueue = new Queue<string>();
private static readonly object ReceiveMsgQueueLock = new object();
private bool _isClose = false;
private readonly byte[] _receiveMsgBuffer = new byte[1024 * 1024];
private int _receiveMsgLength = 0;
private const int UpdateCheckCount = 5;
private int _currUpdateCheckCount = 0;
private void Awake()
{
_instance = this;
}
private void Update()
{
while (_currUpdateCheckCount<UpdateCheckCount)
{
_currUpdateCheckCount++;
lock (ReceiveMsgQueueLock)
{
if (_receiveMsgQueue.Count<=0)
{
_currUpdateCheckCount = 0;
break;
}
string msg = _receiveMsgQueue.Dequeue();
Debug.Log(msg);
}
}
}
public void Connect(string ip, int port)
{
if (_socket is {Connected: true})
return;
_isClose = false;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
_socket.Connect(new IPEndPoint(IPAddress.Parse(ip),port));
ThreadPool.QueueUserWorkItem(CheckSendMsgQueue);
ThreadPool.QueueUserWorkItem(CheckReceiveMsgQueue);
}
catch (Exception e)
{
Debug.Log(e);
}
}
public void SendMsg(string msg)
{
lock (SendMsgQueueLock)
{
_sendMsgQueue.Enqueue(msg);
}
}
private void CheckSendMsgQueue(object obj)
{
while (!_isClose)
{
try
{
lock (SendMsgQueueLock)
{
if (_sendMsgQueue.Count>0)
{
_socket.Send(Encoding.UTF8.GetBytes(_sendMsgQueue.Dequeue()));
}
}
}
catch (Exception e)
{
Debug.Log(e);
return;
}
}
}
private void CheckReceiveMsgQueue(object obj)
{
while (!_isClose)
{
try
{
if (_socket.Available > 0)
{
_receiveMsgLength = _socket.Receive(_receiveMsgBuffer);
lock (ReceiveMsgQueueLock)
{
_receiveMsgQueue.Enqueue(Encoding.UTF8.GetString(_receiveMsgBuffer,0,_receiveMsgLength));
}
}
}
catch (Exception e)
{
Debug.Log(e);
return;
}
}
}
public void Close()
{
_isClose = true;
if (_socket!=null)
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
_socket = null;
}
private void OnDestroy()
{
Close();
}
}