Unity tcp 聊天室

1.服务端

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

/// <summary>
/// 使用socket_tcp编写Unity聊天室的服务器端
/// </summary>
namespace Chatroom_Server
{
    class Program
    {
        /// <summary>
        /// 服务器计算机所在的ip和端口
        /// </summary>
        public const string m_ipAddress = "192.168.0.154";
        public const int m_port = 7788;

        /// <summary>
        /// 服务器Socket对象
        /// </summary>
        static Socket m_tcpServerServer;

        /// <summary>
        /// 存储连接上服务器的客户端
        /// </summary>
        static List<Client> m_clientList = new List<Client>();

        /// <summary>
        /// 线程:专门用来处理客户端的连接
        /// </summary>
        static Thread m_ServerThread;

        static void Main(string[] args)
        {
            //1.创建一个Socket对象作为服务器,可以监听客户端的连接和发送的消息
            //构造函数参数:联网类型(互联网网络,可以内网也可以外网)、数据传输方式(流)、协议(Tcp)
            m_tcpServerServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //2.设置网络连接地址和端口
            //Ip地址是本机的联网地址,让别的计算机找到本计算机
            //端口随便设置,最好4位数,数字越长越不容易被占用,端口是本计算机中程序的识别标记
            //m_tcpServerServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.108"), 2345));
            m_tcpServerServer.Bind(new IPEndPoint(IPAddress.Parse(m_ipAddress), m_port));

            //3.设置监听数量上限
            m_tcpServerServer.Listen(100);
            Console.WriteLine("server running...");

            //创建一个线程专门用来监听客户端的连接从而不影响下面要执行的代码
            m_ServerThread = new Thread(AcceptClientConnect);
            //4.开始监听
            m_ServerThread.Start();
        }

        static void AcceptClientConnect()
        {
            while (true)
            {
                //监听客户端连接,每连接上一个客户端,就创建一个Socket对象与之对应,将Socket对象和连接上来的客户端联系起来
                //如果没有客户端连接上来,那么程序会一直停在这里等待
                Console.WriteLine("waiting client connect!");
                Socket tcpClientSocket = m_tcpServerServer.Accept();
                //获取客户端的IP和端口
                IPEndPoint ipEndClient = (IPEndPoint)tcpClientSocket.RemoteEndPoint;
                //输出客户端的IP和端口
                Console.WriteLine("Connect with " + ipEndClient.Address.ToString() + ":" + ipEndClient.Port.ToString());
                //把与每个客户端通信的逻辑(收发消息)放到Client类里面进行处理
                Client client = new Client(tcpClientSocket);
                m_clientList.Add(client);
            }
        }

        /// <summary>
        /// 将接收的消息广播出去,将消息显示在每个客户端的聊天屏幕上
        /// </summary>
        /// <param name="message"></param>
        public static void BroadCastMessage(string message)
        {
            if (m_clientList == null || m_clientList.Count == 0)
                return;

            byte[] data = Encoding.UTF8.GetBytes(message);

            //用来存储已经断开连接的客户端
            List<Client> notConnected = new List<Client>();
            foreach (var client in m_clientList)
            {
                //广播
                if (client.IsConnected)
                {
                    client.SendMessageToClient(data);
                }
                else
                {
                    notConnected.Add(client);
                }
            }
            //从连接列表中删除已经断开的客户端
            foreach (var item in notConnected)
            {
                m_clientList.Remove(item);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Chatroom_Server
{
    /// <summary>
    /// 用来和客户端通信的单元,每一个客户端对应一个单元,每个单元都会开启一个单独的线程去接收和处理消息
    /// </summary>
    class Client
    {
        /// <summary>
        /// 保存与连接上来的客户端对应的Socket对象
        /// </summary>
        private Socket m_clientSocket;

        /// <summary>
        /// 获取Socket是否连接
        /// </summary>
        public bool IsConnected
        {
            get { return m_clientSocket.Connected; }
        }

        private Thread m_curThread;
        private byte[] m_data = new byte[1024];
        public Client(Socket s)
        {
            //保存
            m_clientSocket = s;

            //创建一个线程并设置线程执行的函数,然后开启线程
            m_curThread = new Thread(ReceiveMessage);
            m_curThread.Start();
        }

        void ReceiveMessage()
        {
            while (true)
            {
                //在接收消息之前要判断Socket是否连接
                if (m_clientSocket.Poll(10, SelectMode.SelectRead))//试图读取客户端数据,如果10毫秒内读取不到,那么判断已经断开连接,返回true
                {
                    m_clientSocket.Shutdown(SocketShutdown.Both);
                    m_clientSocket.Close();
                    break;
                }

                //接收消息
                int length = m_clientSocket.Receive(m_data);//如果没有接收到消息,程序会一直在这里等待
                //Console.WriteLine(m_data.Length);//会输出1024
                string message = Encoding.UTF8.GetString(m_data, 0, length);

                //获取客户端的IP和端口
                IPEndPoint ipEndClient = (IPEndPoint)m_clientSocket.RemoteEndPoint;
                //输出客户端的IP和端口
                Console.WriteLine(ipEndClient.Address.ToString() + ":" + "接收到的消息" + message);
                //将接收的消息广播出去,将消息显示在每个客户端的聊天屏幕上
                Program.BroadCastMessage(ipEndClient.Address.ToString()  + "发送的消息"+ ":" + message);

                //居然发送的消息都是byte数组,为什么不把接收到的数组不转换直接广播出去呢?而是要转换成字符串再转换成byte数组?
                //原因:1.直接发送出去会是一个1024字节大小的数组,每次其实都没有这么大的数据传输,直接这样浪费流量
                //原因:2.直接发送出去会是一个1024字节大小的数组,客户端那边接收的时候也会直接接收一个1024字节的数组,哪怕数组后面没有数据
                //原因:3.直接发送出去会是一个1024字节大小的数组,这个数组由于没有清理过,在接收消息后,数组后面的元素里面可能存储有上一次的数据,导致客户端接收消息混乱(比如说第一条消息比第二条消息长的时候)
                //原因:4.转换一次后传输的数据就变小了,而且客户端获得的数据就会刚好是有用的那一段
            }
        }

        / <summary>
        / 发送消息给对应的客户端()
        / </summary>
        / <param name="message"></param>
        //public void SendMessageToClient(string message)
        //{
        //    byte[] data = Encoding.UTF8.GetBytes(message);
        //    m_clientSocket.Send(data);
        //}
        /// <summary>
        /// 将上面的函数再做一次优化,将字符串转换为byte数组的过程移到广播那边去,这样局只需要转换一次就能发送给所有的客户端了
        /// </summary>
        /// <param name="message"></param>
        public void SendMessageToClient(byte[] messageData)
        {
            m_clientSocket.Send(messageData);
        }
    }
}

2.客户端

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

public class ChatManager : MonoBehaviour
{
    /// <summary>
    /// 服务器计算机所在的ip和端口
    /// </summary>
    public const string m_ipAddress = "192.168.0.154";
    public const int m_port = 7788;

    private Socket m_clientSocket;
    private Thread m_reCeiveThread;
    private byte[] m_msgData = new byte[1024];//消息数据容器
    private string m_message;//保存消息,因为在线程里面不允许直接操作Unity组件

    public InputField m_input;
    public Text m_label;

    // Use this for initialization
    void Start()
    {
         m_label.text = "";
        ConnentToServer();

        //开启一个线程专门用于接收消息
        m_reCeiveThread = new Thread(ReceiveMessage);
        m_reCeiveThread.Start();
    }

    // Update is called once per frame
    void Update()
    {
        if (!string.IsNullOrEmpty(m_message))
        {
            m_label.text += "\n" + m_message;
            Debug.Log("m_message:" + m_message);
            m_message = "";
        }
    }

    /// <summary>
    /// 自定义的函数,连接到服务器
    /// </summary>
    public void ConnentToServer()
    {
        m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //跟服务器建立连接
        Debug.Log("开始连接服务器");
        //clientSocket.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.108"), 2345));
        m_clientSocket.Connect(new IPEndPoint(IPAddress.Parse(m_ipAddress), m_port));
        Debug.Log("连接服务器执行完毕");
    }

    /// <summary>
    /// 向服务器发送消息
    /// </summary>
    /// <param name="message"></param>
    void SendMessageToServer(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        m_clientSocket.Send(data);
    }

    /// <summary>
    /// 点击发送按钮事件
    /// </summary>
    public void OnClickBtnSend()
    {
        string message = m_input.text;
        if (string.IsNullOrEmpty(message))
        {
            Debug.LogWarning("发送消息不能为空");
            return;
        }
        SendMessageToServer(message);
        m_input.text = "";
    }

    void ReceiveMessage()
    {
        while (true)
        {
            //在接受消息之前判断Socket是否连接
            if (m_clientSocket.Connected == false)
                break;
            int length = m_clientSocket.Receive(m_msgData);//这里会等待接收消息,程序暂停,只有接收到消息后才会继续执行
            m_message = Encoding.UTF8.GetString(m_msgData, 0, length);
        }
    }

    void OnDestroy()
    {
        //禁用Socket的发送和接收功能
        m_clientSocket.Shutdown(SocketShutdown.Both);
        //关闭Socket
        m_clientSocket.Close();
    }
}

 

一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值