基于☀️TCP/IP协议的聊天实例

在这里插入图片描述 

目录

🟥 认识Socket

🟧 创建一个Socket服务器

1️⃣ 服务器端

2️⃣ Unity端(客户端)

🟨 创建异步Socket Conn类

🟩 创建异步Socket Sever类

1️⃣ 第一步:添加Server类

2️⃣ 第二步:改Main方法

3️⃣ 第三步:调试

🟦 实战:开发一款多人聊天室


🟥 认识Socket

Skcket:套接字

Socket怎样传输数据?

客户端:

1、链接:connect,连接到服务器Accept

2、写入:write——服务器read    往服务器发送请求/向服务器写入账号密码等

3、读取:read——服务器write    客户端读取服务器发来的请求,根据发来的信息进行下一步操作

4、关闭:close——服务器close    如果一直连接socket,会占用资源

服务端:
1、绑定:Bind

2、监听:Listen

ps:1,2步是先把服务器开启起来进行监听,这样客户端才能访问进来

3、创建一个新的链接:Accept。因为服务器不可能只和一个客户端连接,所以和当前客户端建立一个新的连接,当再来别的客户端连接时,再创建新的链接

4、读取:Read    当收到客户端write请求时,读取数据库是否有该账号密码等,进行数据的验证

5、写入:write    当服务器在数据库读取到数据时,服务器返回/发送请求(查询到信息)给客户端

6、关闭:close

 

 

🟧 创建一个Socket服务器

注:本段是同步

同步和异步的区别:

同步:只能监听一个

异步:可监听多个(真正根据listdfd.Listen(0);  //监听的个数 0代表不限制,同步模式即使这样写也只监听一个)

1️⃣ 服务器端

vs安装:.NET桌面开发

VS新建:控制台应用(.NET Framework)  文件名:mysocket

代码如下:

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

namespace mysocket
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket listdfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipAdr = IPAddress.Parse("127.0.0.1"); //服务器地址
            IPEndPoint ipEp = new IPEndPoint(ipAdr, 1234);  //服务器地址+端口号(端口号可随意)
            listdfd.Bind(ipEp);
            listdfd.Listen(0);  //监听的个数 0代表不限制
            Console.WriteLine("服务器启动成功");


            while (true)
            {
                Socket connfd = listdfd.Accept();   //创建一个新的链接
                Console.WriteLine("服务器Accept成功");

                byte[] readbuff = new byte[1024];
                int count = connfd.Receive(readbuff);   //接受客户端信息:有数据发送过来就会接收
                string str = Encoding.Default.GetString(readbuff, 0, count);
                Byte[] bytes = Encoding.Default.GetBytes("服务器输出" + str);
                connfd.Send(bytes);
            }
        }
    }
}

 

 

2️⃣ Unity端(客户端)

build Settings——Resolution and Presentation——Run in background(勾选),允许你的游戏在后台运行。若取消勾选,则切换到后台时,你的游戏将失去响应

创建connect脚本挂载到Camera,代码如下

using UnityEngine;
using System.Net.Sockets;

public class connect : MonoBehaviour {

    Socket socket;

    const int buff_size = 1024;

    byte[] readbuff = new byte[buff_size];

    private void Start()
    {
        connection();
    }

    public void connection()
    {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        string host = "127.0.0.1";
        int port = 1234;

        socket.Connect(host, port);
        print("客户端地址" + socket.LocalEndPoint);  //显示客户端连接信息

        string str = "nihao";
        byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
        socket.Send(bytes);
        int count = socket.Receive(readbuff);
        str = System.Text.Encoding.Default.GetString(readbuff, 0, count);
        print(str);
        socket.Close();
    }
}

此时,调试VS,启动Unity,观察到以下结果

 

 

🟨 创建异步Socket Conn类

在二基础上,打开服务端,添加Conn类,代码如下

using System;
using System.Net.Sockets;

namespace mysocket
{
    /// <summary>
    /// 处理客户端链接的类
    /// </summary>
    class Conn
    {
        //定义缓冲区
        public const int buff_size = 1024;
        //定义服务器
        public Socket socket;
        //是否使用
        public bool isUse = false;
        public byte[] readBuff = new byte[buff_size];
        public int buffCount = 0;

        public Conn()
        {
            readBuff = new byte[buff_size];
        }

        //初始化
        public void Init(Socket socket)
        {
            this.socket = socket;
            isUse = true;
            buffCount = 0;
        }

        //缓冲所剩余的字节数
        public int BuffRemain()
        {
            return buff_size - buffCount;
        }

        //获取客户端地址
        public string GetAdress()
        {
            if (!isUse)
                return "无法获取地址";
            return socket.RemoteEndPoint.ToString();
        }

        //关闭连接
        public void Close()
        {
            if (!isUse)
                return;
            Console.WriteLine(GetAdress() + "与服务器断开连接");
            socket.Close();
            isUse = false;
        }
    }
}

 

 

🟩 创建异步Socket Sever类

1️⃣ 第一步:添加Server类

在第三基础上,同样在服务器端添加Server类,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;

namespace mysocket
{
    class Server
    {
        //监听嵌套字
        public Socket listedfd;
        //客户端连接
        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
            listedfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //Bind
            IPAddress ipAdr = IPAddress.Parse(host);
            IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
            listedfd.Bind(ipEp);

            //开启监听
            listedfd.Listen(maxConn);

            //开启接收数据
            listedfd.BeginAccept(AcceptCb, null);

            Console.WriteLine("服务器启动成功");
        }


        //回调,异步接收客户端的数据,循环去实现
        void AcceptCb(IAsyncResult ar)
        {
            try
            {
                Socket socket = listedfd.EndAccept(ar);
                int index = NewIndex();
                if (index < 0)
                {
                    socket.Close();
                    Console.WriteLine("警告连接已满");
                }
                else
                {
                    Conn conn = conns[index];
                    conn.Init(socket);
                    string adr = conn.GetAdress();
                    Console.WriteLine("有客户端连接" + adr + "ID号为" + index);

                    conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);

                    //再次调用,实现循环
                    listedfd.BeginAccept(AcceptCb, null);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("回调失败" + ex.Message);
            }
        }

        void ReceiveCb(IAsyncResult ar)
        {
            //异步的状态
            Conn conn = (Conn)ar.AsyncState;

            try
            {
                int count = conn.socket.EndReceive(ar);

                if (count <= 0)
                {
                    Console.WriteLine(conn.GetAdress() + "与服务器断开连接");
                    conn.Close();
                    return;
                }

                string str = Encoding.Default.GetString(conn.readBuff, 0, count);
                Console.WriteLine("收到" + conn.GetAdress() + "发送过来的数据" + str);
                str = conn.GetAdress() + ":" + str;
                byte[] bytes = Encoding.Default.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.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
            }
            catch(Exception ex)
            {
                Console.WriteLine("收到" + conn.GetAdress() + "断开连接"+ex.Message);
                conn.Close();
            }
        }

    }
}

 

 

2️⃣ 第二步:改Main方法

将Program的Main()内方法删掉,改为如下异步方法(原内容为同步)

using System;

namespace mysocket
{
    class Program
    {
        static void Main(string[] args)
        {
            Server server = new Server();
            server.Start("127.0.0.1", 1234);

            while (true)
            {
                string str = Console.ReadLine();

                switch (str)
                {
                    case "quit":    //关闭socket
                        return;
                }
            }
        }
    }
}

 

 

3️⃣ 第三步:调试

在Main界面调试,若出现“服务器启动成功”,则代表成功

 

 

🟦 实战:开发一款多人聊天室

基于以上内容,打开Unity的客户端,将connect脚本内容改为如下所示:

using UnityEngine;
using System.Net.Sockets;
using UnityEngine.UI;
using System;

public class connect : MonoBehaviour {
    //服务器地址、服务器端口、聊天输入框
    public InputField hostInput, portInput,TextInput;

    //客户端收到的信息
    public Text connecttext;
    public Text txtstr;     //用于UI显示服务器发送过来的数据
    public string serverstr;    //注:必须为public!服务器发送回来的数据

    //Socket
    Socket socket;
    const int buff_size = 1024;
    public byte[] readbuff = new byte[buff_size];

    private void Update()
    {
        txtstr.text = serverstr;
    }

    //连接服务器,绑定“连接”按钮
    public void Connection()
    {
        txtstr.text = "";
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        //开始连接服务器
        string host = hostInput.text;
        int port = int.Parse(portInput.text);   //转化为int

        socket.Connect(host, port);
        print("客户端地址" + socket.LocalEndPoint);  //输出:客户端地址127.0.0.1:54000
        connecttext.text = socket.LocalEndPoint.ToString();

        //开始接收数据
        //public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socket_flags, AsyncCallback callback, object state);
        socket.BeginReceive(readbuff, 0, buff_size, SocketFlags.None, ReceiveCb, null);
    }

    //回调方法
    void ReceiveCb(IAsyncResult ar)
    {
        try
        {
            //接收数据的大小
            int count = socket.EndReceive(ar);
            string str = System.Text.Encoding.Default.GetString(readbuff, 0, count);

            if (serverstr.Length > 300) serverstr = "";
            serverstr += str + "\n";                //服务器发送回来的数据(经过加换行处理,且公用放在外面)+=接收数据+ "\n"

            //继续接收数据
            socket.BeginReceive(readbuff, 0, buff_size, SocketFlags.None, ReceiveCb, null);
        }
        catch(Exception ex)
        {
            txtstr.text = "连接已经断开" + ex.Message;
            socket.Close();
        }
    }

    public void Send()
    {
        string str = TextInput.text;
        byte[] bytes = System.Text.Encoding.Default.GetBytes(str);

        try
        {
            socket.Send(bytes);
        }
        catch
        {

        }
    }
}

Unity界面如下

现在运行Unity,输入127.0.0.1,端口1234,即可开始通讯

unity发布exe,运行多个exe,可在多个客户端之间进行实时通讯

 

 

 

大家还有什么问题,欢迎在下方留言!


 

在这里插入图片描述


如果你有 技术的问题 或 项目开发

都可以加下方联系方式

和我聊一聊你的故事🧡

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值