Unity基于TCP/IP的小聊天室实现

关于socket类的更多了解:https://msdn.microsoft.com/zh-cn/library/System.Net.Sockets.Socket(v=vs.110).aspx

要发送字节数组封装类
现在的功能只是发送字符串,也可以改造成传输更多数据类型的类

using System;
using System.Net.Sockets;

//为了避免粘包半包情况
//这个类用来封装字节流,主要分为header+body,header:记录信息包的长度,body:要发送的信息
//TCP每次先读取流中的4个字节,了解到这条信息的长度后,读取同长度的字节流
public class XHNetBits
{

    public Socket handlerSocket;

    private byte[] _bytes;
    public byte[] bytes
    {
        get { return _bytes; }
    }
    private int MAX_LEN = 1024;

    //body的长度
    public int bodyLen
    {
        get
        {
            int bodyLen = BitConverter.ToInt32(_bytes, 0);
            return bodyLen;
        }
    }

    public const int HEAD_LEN = 4;
    private int _currentBodyLen = 0;



    public XHNetBits()
    {
        _bytes = new byte[MAX_LEN];
    }

    //传入字节数组,直接形成header+body字节数组
    public XHNetBits(byte[] bs)
    {
        _bytes = new byte[HEAD_LEN + bs.Length];
        byte[] headBs = BitConverter.GetBytes(bs.Length);
        headBs.CopyTo(_bytes, 0);
        bs.CopyTo(_bytes, HEAD_LEN);

    }

    public void WriteUShort(ushort number)
    {
        byte[] bs = System.BitConverter.GetBytes(number);
        if (bs.Length + _currentBodyLen > MAX_LEN)
        {
            return;
        }
        bs.CopyTo(_bytes, HEAD_LEN + _currentBodyLen);
        _currentBodyLen += bs.Length;
    }

    public void WriteStream(string str)
    {
        int len = System.Text.UTF8Encoding.UTF8.GetByteCount(str);
        System.Text.UTF8Encoding.UTF8.GetBytes(str, 0, str.Length, _bytes, HEAD_LEN + _currentBodyLen);
        _currentBodyLen += len;
    }

    public ushort ReadUshort()
    {
        ushort len = System.BitConverter.ToUInt16(_bytes, 0);
        return len;
    }


    public string ReadString()
    {
        ushort len = ReadUshort();
        string s = System.Text.UTF8Encoding.UTF8.GetString(_bytes, HEAD_LEN, len);
        return s;

    }
}

unity客户端类:
挂在如图ui上
这里写图片描述

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

public class MyChatClient : MonoBehaviour
{


    private InputField _input;
    private Button _btn;
    public Text _msg;

    MyTCPClient _tcpClient;

    public string msg;

    public static MyChatClient Instance = null;

    void Awake()
    {

        if (Instance == null)
            Instance = this;
    }


    void Update()
    {
        _msg.text = msg;

    }


    void Start()
    {

        _input = transform.FindChild("input").GetComponent<InputField>();
        _btn = transform.FindChild("send").GetComponent<Button>();
        _msg = transform.FindChild("msg").GetComponent<Text>();

        _btn.onClick.AddListener(delegate() { OnSend(); });

        _tcpClient = new MyTCPClient();
        _tcpClient.Connect("127.0.0.1", 10001);

        _msg.text = "msg";

    }

    public void OnSend()
    {

        string msg = _input.text;
        _tcpClient.Send(msg);

    }



    public class MyTCPClient
    {

        private Socket _socket = null;

        //连接服务器
        public bool Connect(string ip, int port)
        {

            IPAddress address = IPAddress.Parse(ip);
            IPEndPoint endP = new IPEndPoint(address, port);

            try
            {
                while (true)
                {
                    _socket = new Socket(endP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    //异步方法,连接上后调用ConnectionCallback
                    _socket.BeginConnect(endP, new System.AsyncCallback(ConnectionCallback), _socket);
                    break;
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e);
                return false;
            }

            return true;

        }

        private void ConnectionCallback(System.IAsyncResult asy)
        {
            Debug.Log("c b");

            Socket handlerSocket = (Socket)asy.AsyncState;

            //完成连接服务器
            handlerSocket.EndConnect(asy);
            handlerSocket.SendTimeout = 3;
            handlerSocket.ReceiveTimeout = 3;

            XHNetBits netBits = new XHNetBits();
            netBits.handlerSocket = handlerSocket;

            //开始接收四个字节,这四个字节是int,是这个信息流的长度
            _socket.BeginReceive(
                netBits.bytes,
                0,
                XHNetBits.HEAD_LEN,
                SocketFlags.None,
                new System.AsyncCallback(ReceiveHeader),
                netBits
                );

        }

        //以下使用ReceiveHeader和ReceiveBody连个方法循环读取流,
        //在TCP中,客户端和监听端的发送和接收不一定对应,TCP的传输是流的概念,不是发送次数,所以尽管读取流
        private void ReceiveHeader(System.IAsyncResult result)
        {

            XHNetBits netBits = (XHNetBits)result.AsyncState;

            try
            {
                int read = netBits.handlerSocket.EndReceive(result);
                if (read < 1)
                {
                    Debug.Log("失去连接");
                    return;
                }

                //根据header 描述的长度读取body
                netBits.handlerSocket.BeginReceive(
                    netBits.bytes,
                    XHNetBits.HEAD_LEN,
                    XHNetBits.HEAD_LEN + netBits.bodyLen,
                    SocketFlags.None,
                    new System.AsyncCallback(ReceviBody),
                    netBits
                    );

            }
            catch (System.Exception e)
            {
                Debug.Log(e);
            }

        }
        private void ReceviBody(System.IAsyncResult result)
        {

            XHNetBits netBits = (XHNetBits)result.AsyncState;

            try
            {
                int read = netBits.handlerSocket.EndReceive(result);
                if (read < 1)
                {
                    Debug.Log("失去连接");
                    return;
                }
                string msg = netBits.ReadString();
                Debug.Log("收到消息--------->>" + msg);
                MyChatClient.Instance.msg = msg;


                //接收完一对header和body后,继续重新等待下一个流的header
                netBits.handlerSocket.BeginReceive(
                    netBits.bytes,
                    0,
                    XHNetBits.HEAD_LEN,
                    SocketFlags.None,
                    new System.AsyncCallback(ReceiveHeader),
                    netBits
                    );

            }
            catch (System.Exception e)
            {
                Debug.Log(e);
            }

        }

        //发送消息
        public void Send(string msg)
        {


            if (!_socket.Connected)
            {
                Debug.LogError("un connect!");
                return;
            }

            byte[] sendBs = System.Text.Encoding.UTF8.GetBytes(msg);
            XHNetBits netBs = new XHNetBits(sendBs);


            NetworkStream netStream;
            lock (_socket)
            {
                netStream = new NetworkStream(_socket);
            }
            if (netStream.CanWrite)
            {

                try
                {
                    netStream.BeginWrite(netBs.bytes, 0, netBs.bytes.Length, new System.AsyncCallback(SendCallback), netStream);

                }
                catch (System.Exception e)
                {
                    Debug.LogError(e);
                }

            }
            else
            {
                Debug.Log("can not write");
            }


        }

        private void SendCallback(System.IAsyncResult result)
        {
            NetworkStream netStream = (NetworkStream)result.AsyncState;
            try
            {
                Debug.Log("send end !");
                netStream.EndWrite(result);
                netStream.Flush();
                netStream.Close();
            }
            catch (System.Exception e)
            {
                Debug.LogError(e);
            }
        }

    }


}

服务端:

这个是直接在VS2013上直接新建的控制台应用程序:
这里写图片描述

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

namespace MyChatServer02
{
    class Program
    {

        static void Main(string[] args)
        {

            ServerSocket s = new ServerSocket("127.0.0.1", 10001);
            s.Start();
        }


        public class ServerSocket {

           //先进后出queue,保存消息字符串
            private static System.Collections.Queue msgQueue = new System.Collections.Queue();
            //客户端连接成功后,保存服务器分配处理各个客户端的socket
            private List<Socket> _handlerSockets = new List<Socket>();

            //处理消息的线程
            private System.Threading.Thread processThread;


            private int maxConnection = 100;

            //服务端的监听socket
            private Socket _listnerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


            private string ip = null;
            private int port;

            //构造方法
            public ServerSocket(string ip, int port) {

                this.ip = ip;
                this.port = port;

                //开启一个线程,用while等待处理客户端的消息
                processThread = new Thread(new ThreadStart(ProcessMsg));
                processThread.Start();
            }


            private void ProcessMsg() {
                string msg = null;
                while (true) {
                    if (msgQueue.Count < 1)
                        continue;

                    //从queue里获取消息分发给客户端
                    for (msg = (string)msgQueue.Dequeue(); msg != null; )
                    {    
                        for (int i = 0; i < _handlerSockets.Count; i++) {
                            Socket handlerSocket = _handlerSockets[i];

                            if (handlerSocket.Connected)
                            {
                                this.Send(msg, _handlerSockets[i]);
                            }
                            else {
                                _handlerSockets.RemoveAt(i);
                                Console.WriteLine("移除一个已经下线的客户端handler socket!");
                            }


                        }

                        msg = null;
                    }
                }
            }



            public void Start(){

                Console.WriteLine("start...");
                Console.WriteLine("at : " + ip + " : " + port);
                IPAddress address = IPAddress.Parse(this.ip);
                IPEndPoint localIpEnd = new IPEndPoint(address, this.port);



                _listnerSocket.Bind(localIpEnd);
                _listnerSocket.Listen(maxConnection);

                    while (true) {

                       try
                       {
                           //BeginAccept:等待连接,等待一个socket client连接上来
                            _listnerSocket.BeginAccept(new AsyncCallback(AcceptedClient), _listnerSocket);
                            break;
                         }
                      catch (Exception e)
                          {
                              Console.WriteLine("--->" + e);
                          }
                    }

            }
            //IAsyncResult 对象,它存储此异步操作的状态信息以及任何用户定义数据。
            private void AcceptedClient(IAsyncResult ar)
            {

                Console.WriteLine("新连接一个客户端!!");
                try {
                    Socket listener = (Socket)ar.AsyncState;
                    //结束EndAccept,返回一个 Socket 对象,它处理与远程主机(client)的通信。
                    Socket handlerSocket = listener.EndAccept(ar);
                    _handlerSockets.Add(handlerSocket);

                    handlerSocket.SendTimeout = 3;
                    handlerSocket.ReceiveTimeout = 3;

                    XHNetBits netBits = new XHNetBits();
                    netBits.handlerSocket = handlerSocket;

                    //开启读取接受字节
                    handlerSocket.BeginReceive(
                        netBits.bytes,
                        0,
                        XHNetBits.HEAD_LEN,
                        SocketFlags.None,
                        new AsyncCallback(ReceiveHeader),
                        netBits
                        );


                }
                catch (Exception e) {
                    Console.WriteLine(e);
                }
                _listnerSocket.BeginAccept(new AsyncCallback(AcceptedClient), _listnerSocket);
            }


            //以下使用ReceiveHeader和ReceiveBody连个方法循环读取流,
            //在TCP中,客户端和监听端的发送和接收不一定对应,TCP的传输是流的概念,不是发送次数,所以尽管读取流
            private void ReceiveHeader(IAsyncResult result)
            {

                XHNetBits netBits = (XHNetBits)result.AsyncState;
                try
                {
                    int len = netBits.handlerSocket.EndReceive(result);
                    if (len < 1) {
                        Console.WriteLine("read len < 1,1用户下线!");
                        if (_handlerSockets.Contains(netBits.handlerSocket))
                        {
                            _handlerSockets.Remove(netBits.handlerSocket);
                            Console.WriteLine("移除一个已下线的handler socket");
                        }

                        return;
                    }

                    //接收完一对header和body后,继续重新等待下一个流的header
                    netBits.handlerSocket.BeginReceive(
                        netBits.bytes,
                        XHNetBits.HEAD_LEN,
                        XHNetBits.HEAD_LEN + netBits.bodyLen,
                        SocketFlags.None,
                        new AsyncCallback(ReciveBody),
                        netBits
                        );

                }
                catch (Exception e) {
                    Console.WriteLine(e);
                }

            }

            //最后又循环ReceiveHeader
            private void ReciveBody(IAsyncResult result) {

                XHNetBits netBits = (XHNetBits)result.AsyncState;
                try
                {
                    int recvlen = netBits.handlerSocket.EndReceive(result);
                    if (recvlen < 1)
                    {
                        Console.WriteLine("body read len < 1, 1用户下线!");
                        if (_handlerSockets.Contains(netBits.handlerSocket))
                        {
                            _handlerSockets.Remove(netBits.handlerSocket);
                            Console.WriteLine("移除一个已下线的handler socket");
                        }
                    }
                    string body = netBits.ReadString();
                    msgQueue.Enqueue(body);
                    Console.WriteLine(body);

                    //循环开启读取接受字节

                    //开启读取接受字节
                     netBits.handlerSocket.BeginReceive(
                        netBits.bytes,
                        0,
                        XHNetBits.HEAD_LEN,
                        SocketFlags.None,
                        new AsyncCallback(ReceiveHeader),
                        netBits
                        );

                }
                catch (System.Exception e) {
                    Console.WriteLine(e);
                }


            }

            private void Send(String msg, Socket handlerSocket) {

                if (!handlerSocket.Connected) {
                    if (_handlerSockets.Contains(handlerSocket))
                    {
                        _handlerSockets.Remove(handlerSocket);
                        Console.WriteLine("移除一个已下线的handler socket");
                    }
                    Console.WriteLine("此客户端已经下线");
                    return;
                }

                NetworkStream netStream = null;

                lock (handlerSocket)
                {
                    netStream = new NetworkStream(handlerSocket);
                }
                if (netStream.CanWrite) {

                    byte[] msgBs = System.Text.UTF8Encoding.UTF8.GetBytes(msg);
                    int len = msgBs.Length;
                    byte[] lenBs = BitConverter.GetBytes(len);
                    byte[] sendBs = new byte[lenBs.Length + msgBs.Length];
                    lenBs.CopyTo(sendBs, 0);
                    msgBs.CopyTo(sendBs, lenBs.Length);

                    try {

                        netStream.BeginWrite(sendBs, 0, sendBs.Length, new AsyncCallback(SendCallback), netStream);
                    }
                    catch (System.Exception e) {
                        Console.WriteLine(e);
                    }
                }

            }

            private void SendCallback(IAsyncResult result) {
                NetworkStream netStream = (NetworkStream)result.AsyncState;

                try {

                    netStream.EndWrite(result);
                    netStream.Flush();
                    netStream.Close();

                }
                catch (System.Exception e) {
                    Console.WriteLine(e);
                }

            }

       }

    }
}

完成!
这里写图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
抱歉,我是AI语言模型,无法为您提供完整的Unity通过TCP/IP协议的通讯代码。但是,我可以为您提供一些基本的步骤和示例代码,希望能帮到您。 步骤: 1. 创建TCP客户端和服务端 在Unity中,可以使用C#语言的System.Net.Sockets命名空间创建TCP客户端和服务端。客户端和服务端可以在同一台计算机上,也可以在不同的计算机上。 2. 连接 客户端通过连接服务器来建立连接。可以使用TcpClient类的Connect方法。连接成功后,可以使用NetworkStream类的Write方法向服务器发送消息。 3. 通讯 客户端和服务端通过读写流来通讯。可以使用NetworkStream类的Read和Write方法来读取和写入数据。服务器可以使用TcpListener类的AcceptTcpClient方法接受连接请求。 示例代码: 客户端: ``` using System.Net.Sockets; using System.Text; public class Client { public void Connect(string ip, int port) { TcpClient client = new TcpClient(ip, port); NetworkStream stream = client.GetStream(); byte[] message = Encoding.UTF8.GetBytes("Hello server!"); stream.Write(message, 0, message.Length); byte[] buffer = new byte[1024]; int length = stream.Read(buffer, 0, buffer.Length); string response = Encoding.UTF8.GetString(buffer, 0, length); Debug.Log("Server response: " + response); stream.Close(); client.Close(); } } ``` 服务端: ``` using System.Net; using System.Net.Sockets; using System.Text; public class Server { public void Listen(int port) { TcpListener listener = new TcpListener(IPAddress.Any, port); listener.Start(); TcpClient client = listener.AcceptTcpClient(); NetworkStream stream = client.GetStream(); byte[] buffer = new byte[1024]; int length = stream.Read(buffer, 0, buffer.Length); string message = Encoding.UTF8.GetString(buffer, 0, length); Debug.Log("Client message: " + message); byte[] response = Encoding.UTF8.GetBytes("Hello client!"); stream.Write(response, 0, response.Length); stream.Close(); client.Close(); listener.Stop(); } } ``` 以上是简单的示例代码,实际应用中需要考虑更多细节,例如消息的序列化和反序列化、连接的稳定性等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值