3D网络实战学习——网络基础(二)

简单网络程序

同步

服务器

using System;
using System.Net;
using System.Net.Sockets;

namespace _3DNetActualCombat_SimpleServer
{
	class Program
	{
		static void Main(string[] args)
		{
			//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, 8023);
			listenfd.Bind(ipEp);//绑定IP和端口,127.0.0.1指本地地址,一般用于测试
			//listen,开启监听
			listenfd.Listen(0);
			Console.WriteLine("服务器启动成功");

			while (true)
			{
				//Accept,开始接收客户端连接,当没有客户端连接时会卡住不往下执行,
				//这里返回一个新客户端的Socket
				//对于服务器来说,他有一个监听Socket(listenfd)用来接收客户端的连接,
				//对于每个客户端来说还有一个专门的Socket(Connfd)用来处理该客户端的数据
				Socket connfd = listenfd.Accept();
				Console.WriteLine("服务器Accept");
				//Read(Receive)接收数据
				byte[] readBuff = new byte[1024];
				int count = connfd.Receive(readBuff);
				string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
				Console.WriteLine("服务器接收:"+str);
				//send  发送数据
				str = System.DateTime.Now.ToString();
				byte[] bytes = System.Text.Encoding.UTF8.GetBytes("接收到消息" + str);
				connfd.Send(bytes);
			}
		}
	}
}

客户端

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
public class net : MonoBehaviour
{
	//与服务器端的套接字
	Socket socket;
	//服务端的IP和端口
	public InputField hostInput;
	public InputField portInput;

	public Text recvText;
	public Text clientText;
	//接收缓冲区
	const int BUFFER_SIZE = 1024;
	byte[] readBuff = new byte[BUFFER_SIZE];

	/// 
	/// 连接,挂载到连接按钮上
	/// 
	public void Connetion()
	{
		socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		//connect
		string host = hostInput.text;
		int port = int.Parse(portInput.text);
		socket.Connect(host, port);
		clientText.text = "客户端地址" + socket.LocalEndPoint.ToString();
		//send
		string str = "Hello Unity!";
		byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
		socket.Send(bytes);
		//recv
		int count = socket.Receive(readBuff);
		str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
		recvText.text = str;
		socket.Close();

	}
}

客户端Hierarchy设置
3D网络实战学习——网络基础(二)_第1张图片

异步

API

同步模式中,服务器使用Accept接受连接请求,客户端使用Connect连接。
相对的在异步模式下,服务器可以使用BeginAccept和EndAccept方法完成接受客户端请求。

相应API:

BeginAccept(AsyncCallbaxk asyncCallback,Object state)
	asyncCallback:代表回调函数
	state:表示状态信息,必须保证state中包含socket的句柄。

例子:

listenfd.BeginAccect(AcceptCb,null);

private void AcceptCb(IAsyncResult ar)
{
	Socket socket = listenfd.EndAccept(ar);
}
与BeginAccept相似,BeginReceive实现的是异步数据接收

API

public IAsyncResult BeginReceive(
	byte[] buffer,
	int offset,
	int size,
	SocketFlags socketFlags,
	AsyncCallback callback,
	object stat
	)
buffer:Byte类型的数组,用于存储接受到的数据
offset:buffer参数中存储数据的位置,从0开始计数
size:最多接受的字节数
socketFlags:SocketFlags值的按位组合,这里设置为0
callback:回调函数
state:一个用户定义对象,其中包含接收操作的相关信息。当操作完成时,此对象会被传递给EndReceive委托

服务器端Conn

服务器要处理多个客户端消息,他需要用一个数组来维护所有的客户端的连接。每个客户端都有自己的Socket和缓冲区,这里我们写个Conn类来表示客户端链接。

class Conn
	{
		//常量
		public const int BUFFER_SIZE = 1024;
		//Socket
		public Socket socket;
		//是否使用
		public bool isUse = false;
		//Buff
		public byte[] readBuff = new byte[BUFFER_SIZE];
		public int bufferCount = 0;
		//构造函数
		public Conn()
		{
			readBuff = new byte[BUFFER_SIZE];
		}
		/// 
		/// 初始化
		/// 
		/// 
		public void Init(Socket socket)
		{
			this.socket = socket;
			isUse = true;
			bufferCount = 0;
		}
		/// 
		/// 缓冲区剩余的字节数
		/// 
		/// 
		public int BuffRemain()
		{
			return BUFFER_SIZE - bufferCount;
		}
		/// 
		/// 获取客户端地址
		/// 
		/// 
		public string GetAdress()
		{
			if (!isUse)
				return "无法获取地址";
			return socket.RemoteEndPoint.ToString();
		}
		//关闭
		public void Close()
		{
			if (!isUse)
				return;
			Console.WriteLine(GetAdress()+"断开连接");
			socket.Close();
			isUse = false;
		}
	}

服务器端Serv

编写Serv类,它包含一个Conn类型的对象池,用于维护客户端链接。
在Start方法中,服务器将经历Socket、Bind、Listen,然后调用BeginAccept开始异步处理客户端的连接。
class Serv
	{
		//监听嵌套字
		public Socket listenfd;
		//客户端连接
		public Conn[] conns;
		//最大连接数
		public int maxConn = 50;
		/// 
		/// 获取连接池索引,返回负数表示获取失败
		/// 
		/// 
		public int NewIndex()
		{
			if (conns == null) return -1;
			for (int i = 0; i < conns.Length; i++)
			{
				if(conns[i] == null)
				{
					conns[i] = new Conn();
					return i;
				}
				else if(conns[i].isUse == false)
				{
					return i;
				}
			}
			return -1;
		}
		/// 
		/// 开启服务器
		/// 
		/// 
		/// 
		public void Start(string host,int port)
		{
			//连接池
			conns = new Conn[maxConn];
			for (int i = 0; i < maxConn; i++)
			{
				conns[i] = new Conn();
			}
			//Socket
			listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			//bind
			IPAddress ipAdr = IPAddress.Parse(host);
			IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
			listenfd.Bind(ipEp);
			//listen
			listenfd.Listen(maxConn);
			//Accept
			listenfd.BeginAccept(AcceptCb, null);
			Console.WriteLine("服务器启动成功");
		}
		/// 
		/// Accept回调
		/// 
		/// 
		private void AcceptCb(IAsyncResult ar)
		{
			try
			{
				Socket socket = listenfd.EndAccept(ar);
				int index = NewIndex();
				//如果连接处已满,则拒绝连接
				if (index < 0)
				{
					socket.Close();
					Console.WriteLine("连接已满");
				}
				else
				{
					Conn conn = conns[index];
					conn.Init(socket);
					//给新的连接分配conn
					string adr = conn.GetAdress();
					Console.WriteLine("客户端链接"+adr+"conn池ID:"+index);
					conn.socket.BeginReceive(conn.readBuff, conn.bufferCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
				}
				listenfd.BeginAccept(AcceptCb, null);
			}
			catch (Exception e)
			{
				Console.WriteLine("AcceptCb失败:"+e.Message);
			}
		}
		/// 
		/// BeginReceive回调
		/// 
		/// 
		private void ReceiveCb(IAsyncResult ar)
		{
			//获取BeginReceive传入的Conn对象
			Conn conn = (Conn)ar.AsyncState;
			try
			{
				//获取接收的字节数
				int count = conn.socket.EndReceive(ar);
				//关闭信号
				if(count == 0)
				{
					Console.WriteLine("收到"+conn.GetAdress()+"断开连接");
					conn.Close();
					return;
				}
				//数据处理
				string str = System.Text.Encoding.UTF8.GetString(conn.readBuff, 0, count);
				Console.WriteLine("收到"+conn.GetAdress()+"数据:"+str);

				str = conn.GetAdress() + ":" + str;
				byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);

				//广播
				for (int i = 0; i < conns.Length; i++)
				{
					if (conns[i] == null)
						continue;
					if (!conns[i].isUse)
						continue;
					Console.WriteLine("将消息传播给"+conns[i].GetAdress());
					conns[i].socket.Send(bytes);
				}
				//继续接收
				conn.socket.BeginReceive(conn.readBuff, conn.bufferCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
			}
			catch (Exception e)
			{
				Console.WriteLine(conn.GetAdress()+"断开连接");
				conn.Close();
			}
		}

	}

开启服务器Main

static void Main(string[] args)
		{
			Serv serv = new Serv();
			serv.Start("127.0.0.1", 8023);

			while (true)
			{
				string str = Console.ReadLine();
				switch (str)
				{
					case "quit":
						return;
				}
			}
		}

客户端

#region 异步
	//与服务器端的套接字
	Socket socket;
	//服务端的IP和端口
	public InputField hostInput;
	public InputField portInput;

	public Text recvText;
	public Text clientText;
	//客户端收到的消息
	public string recvStr;
	//聊天输入框
	public InputField textInput;
	//接收缓冲区
	const int BUFFER_SIZE = 1024;
	byte[] readBuff = new byte[BUFFER_SIZE];

	/// 
	/// 连接
	/// 
	public void Connetion()
	{
		recvText.text = "";

		socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		//connect
		string host = hostInput.text;
		int port = int.Parse(portInput.text);
		socket.Connect(host, port);
		clientText.text = "客户端地址" + socket.LocalEndPoint.ToString();
		//Recv
		socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null);

	}
	private void ReceiveCb(IAsyncResult ar)
	{
		try
		{
			//接收数据的大小
			int count = socket.EndReceive(ar);
			//数据处理
			string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
			if (recvStr.Length > 300)  recvStr = "";

			recvStr += str + "\n";

			//继续接收
			socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null);
		}
		catch (Exception e)
		{
			recvText.text = "链接已断开";
			socket.Close();
		}
	}
	//需要挂在发送按钮上
	public void Send()
	{
		string str = textInput.text;
		byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
		try
		{
			socket.Send(bytes);
		}
		catch 
		{
		}
	}

	private void Update()
	{
		recvText.text = recvStr;
	}
	#endregion

成品截图
3D网络实战学习——网络基础(二)_第2张图片

你可能感兴趣的:(Socket,Unity)