C#-socket应用示例(网络聊天)

简介:采用UDP协议,利用Client Server架构(简称C/S架构)进行客户端对服务器的通信,利用服务器间接实现各客户端之间的通信。

注意:Unity发布时,需进行设置,将后台运行打开:PlayerSetting--->Resolution

--->Run in Background

需要的类

一、消息类:用于存放需要发送的信息(包括消息类型、发送者名字、消息内容),并对外提供消息类转为字节数组的方法、字节数组转为消息类的方法

——消息类型用枚举分为客户端上线消息,客户端下线消息,普通消息

——发送的信息用字符串进行发送

————对于字符串的处理(数据协议):

        1)编码

        2)写入字节数组的长度(4字节int型)

        3)写入字节数组

using System;
using System.IO;
using System.Text;
namespace NetWork
{
    public enum MessageType
    {
        //上线
        Online,
        //下线
        Offline,
        //常规
        General
    }
    ///<summary>
    ///聊天消息
    ///<summary>
    public class ChatMessage
    {
        public MessageType Type { get; set; }
        public string SenderName { get; set; }
        public string Content {  get; set; }
        /// <summary>
        /// 对象转换为字节数组
        /// </summary>
        /// <returns>byte[]</returns>
        public byte[] ObjectToBytes()
        {
            //对象变成字节数组 string/int/bool - 二进制写入器->内存流MemroyStream-》 byte[]
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                WriteString(writer, Type.ToString());
                WriteString(writer, SenderName);
                WriteString(writer, Content);
                return stream.ToArray();
            }
        }

        private void WriteString(BinaryWriter writer,string str)
        {
            byte[] typeBTS = Encoding.Unicode.GetBytes(str);
            //写入长度
            writer.Write(typeBTS.Length);
            //写入内容
            writer.Write(typeBTS);
        }
        /// <summary>
        /// 将字节数组转换为对象
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns>ChatMessage</returns>
        public static ChatMessage BytesToObject(byte[] bytes)
        {
            //创建聊天消息对象
             ChatMessage obj = new ChatMessage();
            //byte[]   --内存流--二进制读取器--》string/int/bool
            using(MemoryStream stream = new MemoryStream(bytes))
            {
                BinaryReader reader = new BinaryReader(stream);

                string strType = ReadString(reader);
                obj.Type = (MessageType)Enum.Parse(typeof(MessageType), strType);
                obj.SenderName = ReadString(reader);
                obj.Content = ReadString(reader);
                return obj;
            }
        }

        private static string ReadString(BinaryReader reader)
        {
            int Length = reader.ReadInt32();//自动读4个字节
            byte[] BTS = reader.ReadBytes(Length);
            return Encoding.Unicode.GetString(BTS);
        }
    }
}

二、客户端部分

1、消息发送、接收方法服务类

主要对外提供发送消息的方法,同时开辟线程进行接收消息。利用委托执行接收消息后的消息展示等方法的调用

这是实现通信的主要方法类,做成单例并在跨场景时不销毁。

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace NetWork
{
    ///<summary>
    ///
    ///<summary>
    public class ClientUdpNetworkService : MonoSingleton<ClientUdpNetworkService>
    {
        private UdpClient udpService;
        private Thread threadReceive;
        //创建Socket对象(由登录窗口传递服务端Ip,端口)

        public event EventHandler<MessageArriveEventArgs> MessageArrive;
        public void Initialized(string severIP, int serverPort)
        {
            //跨场景不销毁当前游戏对象
            DontDestroyOnLoad(this.gameObject);
            //创建服务端终结点
            IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(severIP), serverPort);
            udpService = new UdpClient();//随机分配可用的端口
            //与服务端建立连接(没有三次握手)
            udpService.Connect(serverEP);
            threadReceive = new Thread(ReceiveChatMessage);
            threadReceive.Start();
            //发送上线消息
            NotifyServer(MessageType.Online);
        }
        private void NotifyServer(MessageType type)
        {
            SendChatMessage(new ChatMessage() { Type = type});
        }
        //发送数据
        public void SendChatMessage(ChatMessage msg)
        {
            byte[] buffer = msg.ObjectToBytes();
            //备注:发送时不能指定中断(创建Socket对象时,建立了链接)
            udpService.Send(buffer,buffer.Length);
        }
        //接收数据
        private void ReceiveChatMessage()
        {
            while (true)
            {
                IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = udpService.Receive(ref remote);
                ChatMessage msg = ChatMessage.BytesToObject(data);
                //引发事件
                if (MessageArrive == null) continue;
                MessageArriveEventArgs args = new MessageArriveEventArgs
                {
                    ArriveTime = DateTime.Now,
                    Message = msg
                };
                 //在主线程中引用委托方法
                 CrossAssistance.Instance.ExecuteOnMainThread(() => { MessageArrive(this, args); });
            }
        }
        //引发数据(1、事件参数类2、委托3、声明事件4、引发)
        private void OnApplicationQuit()
        {
            //发送下线消息
            NotifyServer(MessageType.Offline);
            threadReceive.Abort();
            udpService.Close();
        }
    }
}

2、消息发送、与接收脚本:调用“1”方法,注册“1”中的委托事件。

using UnityEngine;
using NetWork;
using System;
public class ChatClient:MonoBehaviour
{
    private void Start()
    {
        //通过委托再接收到信息时执行
        //注册事件
        ClientUdpNetworkService.Instance.MessageArrive += DisplayMessage;
    }
    //显示数据
    private void DisplayMessage(object sender, MessageArriveEventArgs args)
    {
        ChatMessage msg = args.Message;
        DateTime dateTime = args.ArriveTime;
    }

    private void OnSendMessageButtonClick()
    {
        ChatMessage message = new ChatMessage()
        {
            Type = MessageType.General,
            SenderName = "……",
            Content = "……"
        };
        ClientUdpNetworkService.Instance.SendChatMessage(message);
    }
}

三、服务端部分

1、消息发送、接收方法服务类

类似客户端部分,添加客户端列表,实现将所收到的消息转发给客户端

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace NetWork
{
    ///<summary>
    ///
    ///<summary>
    public class ServerUdpNetworkService : MonoSingleton<ServerUdpNetworkService>
    {
        private UdpClient udpService;
        private Thread threadReceive;
        //创建Socket对象(由登录窗口传递服务端Ip,端口)

        public event EventHandler<MessageArriveEventArgs> MessageArrive;

        private List<IPEndPoint> allClientEP;
        public override void Init()
        {
            base.Init();
            allClientEP = new List<IPEndPoint>();
        }
        public void Initialized(string severIP, int serverPort)
        {
            //跨场景不销毁当前游戏对象
            DontDestroyOnLoad(this.gameObject);
            //创建服务端终结点
            IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(severIP), serverPort);
            udpService = new UdpClient(serverEP);//随机分配可用的端口

            threadReceive = new Thread(ReceiveChatMessage);
            threadReceive.Start();
        }
        //发送数据
        public void SendChatMessage(ChatMessage msg, IPEndPoint remote)
        {
            byte[] buffer = msg.ObjectToBytes();

            udpService.Send(buffer, buffer.Length, remote);
        }
        //接收数据
        private void ReceiveChatMessage()
        {
            while (true)
            {
                IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = udpService.Receive(ref remote);
                ChatMessage msg = ChatMessage.BytesToObject(data);
                //根据消息类型,执行相关逻辑
                OnMessageArrived(msg,remote);
                //引发事件
                if (MessageArrive == null) continue;
                MessageArriveEventArgs args = new MessageArriveEventArgs
                {
                    ArriveTime = DateTime.Now,
                    Message = msg
                };
                //在主线程中引用委托方法
                CrossAssistance.Instance.ExecuteOnMainThread(() => { MessageArrive(this, args); });
            }
        }
        //引发数据(1、事件参数类2、委托3、声明事件4、引发)
        private void OnApplicationQuit()
        {
            threadReceive.Abort();
            udpService.Close();
        }
        private void OnMessageArrived(ChatMessage msg,IPEndPoint remote)
        {
            switch (msg.Type)
            {
                case MessageType.Offline:
                    //删除客户端
                    allClientEP.Remove(remote);
                    break;
                case MessageType.Online:
                    //添加客户端
                    allClientEP.Add(remote);
                    break;
                case MessageType.General:
                    //转发(需要客户端的终结点)
                    //foreach (var item in allClientEP)
                    //    SendChatMessage(msg,item);
                    allClientEP.ForEach(item => SendChatMessage(msg, item));
                    break;
            }
        }
    }

}

2、消息接收、显示脚本

与客户端部分一样

using UnityEngine;
using NetWork;
using System;
public class ChatServer : MonoBehaviour
{
    private void Start()
    {
        //通过委托再接收到信息时执行
        //注册事件
        ServerUdpNetworkService.Instance.MessageArrive += DisplayMessage;
    }
    //显示数据
    private void DisplayMessage(object sender, MessageArriveEventArgs args)
    {
        ChatMessage msg = args.Message;
        DateTime dateTime = args.ArriveTime;
    }
}

四、注册类脚本(客户端注册和服务端注册)

初始化,使客户端和服务端进行链接

public class GameMain : MonoBehaviour
{
    public string serverIp;
    public int Port;
    //注册
    private void OnEnterClientButtonClick()
    {
        ClientUdpNetworkService.Instance.Initialized(serverIp,Port);
    }
    private void OnEnterServerButtonClick()
    {
        ServerUdpNetworkService.Instance.Initialized(serverIp, Port);
    }
}

  • 10
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值