客户端

 

实现服务端基本逻辑

                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()
    {
        
    }
}

unity客户端与服务器数据同步 unity中服务器和客户端network_unity客户端与服务器数据同步

服务端实现多个客户端的交换

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()
    {
        
    }
}