客户端
实现服务端基本逻辑
1.创建套接字Socket(TCP)
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2.用Bind方法将套接字与本地地址绑定
try
{
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socketTcp.Bind(ipPoint);
}
catch (Exception e)
{
Console.WriteLine("绑定报错" + e.Message);
return;
}
3.用Listen方法监听
socketTcp.Listen(1024);
Console.WriteLine("服务端绑定监听结束,等待客户端连入");
4.用Accept方法等待客户端连接
5.建立连接,Accept返回新套接字
Socket socketClient = socketTcp.Accept();
Console.WriteLine("有客户端连入了");
6.用Send和Receive相关方法收发数据
//发送
socketClient.Send(Encoding.UTF8.GetBytes("欢迎连入服务端"));
//接受
byte[] result = new byte[1024];
//返回值为接受到的字节数
int receiveNum = socketClient.Receive(result);
Console.WriteLine("接受到了{0}发来的消息:{1}",
socketClient.RemoteEndPoint.ToString(),
Encoding.UTF8.GetString(result, 0, receiveNum));
7.用Shutdown方法释放连接
socketClient.Shutdown(SocketShutdown.Both);
8.关闭套接字
socketClient.Close();
实现服务端基本逻辑
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace NetTest
{
class Program
{
static void Main(string[] args)
{
//1.创建套接字Socket
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.用Bind方法将套接字与本地地址绑定(注:这里用try、catch是为了防止端口号已经被使用了
try
{
//指定服务器的IP地址和端口号
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socketTcp.Bind(ipPoint);
}
catch (Exception e)
{
Console.WriteLine("绑定报错" + e.Message);
return;
}
//3.用Listen方法监听,参数代表可以接入客户端最大的数量
socketTcp.Listen(1024);
Console.WriteLine("服务端绑定监听结束,等待客户端连入");
//4.用Accept方法等待客户端连接
//5.建立连接,Accept返回新套接字
Socket socketClient = socketTcp.Accept(); //该函数的执行,会一直坚持到有客户端加入才会继续到下一步
Console.WriteLine("有客户端连入了");
//6.用Send和Receive相关方法收发数据
//发送
socketClient.Send(Encoding.UTF8.GetBytes("欢迎加入服务端"));
//接收
byte[] result = new byte[1024];
//返回值为接受到的字节数
int receiveNum = socketClient.Receive(result);
Console.WriteLine("接受到了{0}发来的消息:{1}",
socketClient.RemoteEndPoint.ToString(),
Encoding.UTF8.GetString(result, 0, receiveNum));
//7.用Shutdown方法释放连接
socketClient.Shutdown(SocketShutdown.Both);
//8.关闭套接字
socketClient.Close();
Console.WriteLine("按任意键退出");
Console.ReadKey();
}
}
}
客户端
实现客户端基本逻辑
1.创建套接字Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2.用Connect方法与服务端相连
//确定服务端的IP和端口
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
try
{
socket.Connect(ipPoint);
}
catch (SocketException e)
{
if (e.ErrorCode == 10061)
print("服务器拒绝连接");
else
print("连接服务器失败" + e.ErrorCode);
return;
}
3.用Send和Receive相关方法收发数据(客户端的 Connect、Send、Receive是会阻塞主线程的,要等到执行完毕才会继续执行后面的内容
//接收数据
byte[] receiveBytes = new byte[1024];
int receiveNum = socket.Receive(receiveBytes);
print("收到服务端发来的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));
//发送数据
socket.Send(Encoding.UTF8.GetBytes("你好,我是唐老狮的客户端"));
4.用Shutdown方法释放连接
socket.Shutdown(SocketShutdown.Both);
5.关闭套接字
socket.Close();
实现客户端基本逻辑
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
public class Lesson6 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//1.创建套接字Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.用Connect方法与服务端相连
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080); //这里面的IP地址和端口号指的是服务端的而不是客户端的
try
{
socket.Connect(ipPoint);
}
catch(SocketException e)
{
if (e.ErrorCode == 10061)
print("服务器拒绝连接");
else
print("连接服务器失败" + e.ErrorCode);
return;
}
//3.用Send和Receive相关方法收发数据
//接收数据
byte[] receiveBytes = new byte[1024];
int receiveNum = socket.Receive(receiveBytes);
print("收到服务端发来的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));
//发送数据
socket.Send(Encoding.UTF8.GetBytes("你好,我是客户端"));
//4.用Shutdown方法释放连接
socket.Shutdown(SocketShutdown.Both);
//5.关闭套接字
socket.Close();
}
// Update is called once per frame
void Update()
{
}
}
服务端实现多个客户端的交换
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;
namespace Test02
{
class Program
{
//设置socket静态变量,方便在全局进行使用
static Socket socket;
//设置一个存放客户端的链表
static List<Socket> clientSockets = new List<Socket>();
static bool IsClose = false;
static void Main(string[] args)
{
//1.建立Socket 绑定 监听
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.Bind(ipPoint);
socket.Listen(1024);
//2.等待客户端连接
//开启多线程接收客户端的连接
Thread acceptThread = new Thread(AcceptClientConnect);
//线程的开启
acceptThread.Start();
//3.收发消息
Thread receiveThread = new Thread(ReceiveMsg);
receiveThread.Start();
//4.关闭相关
while (true)
{
Console.WriteLine(1);
string input = Console.ReadLine();
//定义一个关闭服务器,断开连接
if(input == "Quit")
{
IsClose = true;
//遍历关闭所有与客户端的连接
for(int i = 0; i < clientSockets.Count; i++)
{
clientSockets[i].Shutdown(SocketShutdown.Both);
clientSockets[i].Close();
}
//清空客户端数组
clientSockets.Clear();
IsClose = true;
Console.WriteLine("服务端已关闭");
break;
}
//定义一个规则 广播消息 就是让所有客户端收到服务端发送的消息
else if (input.Substring(0, 2) == "B:")
{
for (int i = 0; i < clientSockets.Count; i++)
{
clientSockets[i].Send(Encoding.UTF8.GetBytes(input.Substring(2)));
}
}
}
}
public static void AcceptClientConnect()
{
while (!IsClose)
{
Socket clientSocket = socket.Accept();
clientSockets.Add(clientSocket);
clientSocket.Send(Encoding.UTF8.GetBytes("欢迎加入服务端"));
}
}
public static void ReceiveMsg()
{
Socket clientSocket;
byte[] result = new byte[1024];
int receiveNum;
int i;
//死循环,一直接收客户端的消息
while (!IsClose)
{
//遍历整个客户端数组看是否有消息传入
for(i = 0; i < clientSockets.Count; i++)
{
clientSocket = clientSockets[i];
//判断该客户端是否返回长度大于0的消息
if(clientSocket.Available > 0)
{
//将从客户端接收到的消息存入字节当中,并且返回收到的字节数
receiveNum = clientSocket.Receive(result);
//使用线程池来处理接收到的消息
ThreadPool.QueueUserWorkItem(HandleMsg, (result, 0, receiveNum));
}
}
}
}
static void HandleMsg(object obj)
{
//将所有的传入的数据强制转换
(Socket s, string str) info = ((Socket s, string str))obj;
Console.WriteLine("收到客户端{0}发来的信息:{1}", info.s.RemoteEndPoint, info.str);
}
}
}
使用面向对象编程的思想封装服务端
对客户端处理的部分
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Test04
{
class ClientSocket
{
//记录当前通讯的客户端的编号和网络
private static int CLIENT_BEGIN_ID = 1;
public int clientID;
public Socket socket;
public ClientSocket(Socket socket)
{
this.clientID = CLIENT_BEGIN_ID;
this.socket = socket;
//每有一个客户端连入,编号就++
++CLIENT_BEGIN_ID;
}
/// <summary>
/// 是否是连接状态
/// </summary>
public bool Connected => this.socket.Connected;
//关闭与客户端的连接
public void Close()
{
//如果客户端的网络连接不是空,就将它变为空
if(socket != null)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
}
//与客户端发送消息
public void Send(string info)
{
//如果不为空就可以进行与客户端的消息发送
if(socket != null)
{
try
{
socket.Send(Encoding.UTF8.GetBytes(info));
}
catch(Exception e)
{
Console.WriteLine("发送消息出错" + e.Message);
Close();
}
}
}
//接收客户端的发来的消息
public void Receive()
{
//如果网络为空就进行返回
if (socket == null)
return;
try
{
//如果传入的数据大于0
if(socket.Available > 0)
{
byte[] result = new byte[1024];
int receiveNum = socket.Receive(result);
//开启线程池一直监听客户端发来的消息
ThreadPool.QueueUserWorkItem(MsgHandle,Encoding.UTF8.GetString(result,0,receiveNum));
}
}catch(Exception e)
{
Console.WriteLine("收发消息出错" + e.Message);
Close();
}
}
private void MsgHandle(object obj)
{
string str = obj as string;
Console.WriteLine("收到客户端{0}发来的消息:{1}", this.socket.RemoteEndPoint, str);
}
}
}
服务端的开启和关闭
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;
namespace Test04
{
class ServerSocket
{
/// <summary>
/// 服务端的Socket网络协议
/// </summary>
public Socket socket;
//字典存储(id为字典编号,数据为客户端)
public Dictionary<int, ClientSocket> clientDic = new Dictionary<int, ClientSocket>();
private bool isClose;
//开启服务器端
public void Start(string ip,int port,int num)
{
isClose = false;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);
socket.Bind(ipPoint);
socket.Listen(num);
ThreadPool.QueueUserWorkItem(Accept);
ThreadPool.QueueUserWorkItem(Receive);
}
public void Close()
{
isClose = true;
foreach(ClientSocket client in clientDic.Values)
{
client.Close();
}
clientDic.Clear();
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
//接受客户端的连入
private void Accept(object obj)
{
while (!isClose)
{
try
{
//连入一个客户端
Socket clientSocket = socket.Accept();
ClientSocket client = new ClientSocket(clientSocket);
client.Send("欢迎连入服务器");
clientDic.Add(client.clientID, client);
}
catch(Exception e)
{
Console.WriteLine("客户端连入报错" + e.Message);
}
}
}
//接受客户端的消息
private void Receive(object obj)
{
while (!isClose)
{
if (clientDic.Count > 0)
{
foreach (ClientSocket client in clientDic.Values)
{
client.Receive();
}
}
}
}
//广播消息
public void Broadcast(string info)
{
foreach(ClientSocket client in clientDic.Values)
{
client.Send(info);
}
}
}
}
服务端的主程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test04
{
class Program
{
static void Main(string[] args)
{
ServerSocket socket = new ServerSocket();
socket.Start("127.0.0.1", 8080, 1024);
Console.WriteLine("服务端开启成功");
while (true)
{
string input = Console.ReadLine();
if(input == "Quit")
{
socket.Close();
}
else if(input.Substring(0,2) == "B:")
{
socket.Broadcast(input.Substring(2));
}
}
}
}
}
客户端实现
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class NetMgr : MonoBehaviour
{
//通过单例让一个类只有一个实例
private static NetMgr instance;
public static NetMgr Instance => instance;
//客户端的Socket
private Socket socket;
//用于发送消息的队列 公共的容器(线程) 主线程往里面放 发送线程从里面取
private Queue<string> sendMsgQueue = new Queue<string>();
//用于接受消息的对象 公共容器(线程) 子线程往里面放 主线程从里面取
private Queue<string> receiveQueue = new Queue<string>();
//用于收消息的容器
private byte[] receiveBytes = new byte[1024];
//返回收到的字节数
private int receiveNum;
//是否连接
private bool isConnected = false;
private void Awake()
{
instance = this;
//防止切换场景网络连接就断了
DontDestroyOnLoad(this.gameObject);
}
private void Update()
{
//如果线程里面有消息,就进行输出
if (receiveQueue.Count > 0)
{
print(receiveQueue.Dequeue());
}
}
public void Connect(string ip,int port)
{
//如果是连接状态 直接返回
if (isConnected)
return;
if (socket == null)
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接服务器
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);
try
{
socket.Connect(ipPoint);
isConnected = true;
//开启发送线程
ThreadPool.QueueUserWorkItem(SendMsg);
//开启接受线程
ThreadPool.QueueUserWorkItem(ReceiveMsg);
}catch(SocketException e)
{
if (e.ErrorCode == 10061)
print("服务器拒绝连接");
else
print("连接失败" + e.ErrorCode + e.Message);
}
}
//发送消息
public void Send(string info)
{
sendMsgQueue.Enqueue(info);
}
private void SendMsg(object obj)
{
while (isConnected)
{
if(sendMsgQueue.Count > 0)
{
socket.Send(Encoding.UTF8.GetBytes(sendMsgQueue.Dequeue()));
}
}
}
//不停的接受消息
private void ReceiveMsg(object obj)
{
while (isConnected)
{
if (socket.Available > 0)
{
receiveNum = socket.Receive(receiveBytes);
//收到消息 解析消息为字符串 并放入公共容器
receiveQueue.Enqueue(Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));
}
}
}
public void Close()
{
if(socket != null)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
isConnected = false;
}
}
private void OnDestroy()
{
Close();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Main : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
if(NetMgr.Instance == null)
{
GameObject obj = new GameObject("Net");
obj.AddComponent<NetMgr>();
}
NetMgr.Instance.Connect("127.0.0.1", 8080);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lesson7 : MonoBehaviour
{
public Button btn;
public InputField input;
// Start is called before the first frame update
void Start()
{
btn.onClick.AddListener(() =>
{
if (input.text != "")
NetMgr.Instance.Send(input.text);
});
}
// Update is called once per frame
void Update()
{
}
}