C#网络聊天室

简述

使用VS控制台应用,开发C#网络通信代码。模拟聊天室。

代码

首先创建一个ServerController的解决方案,使用一下脚本:服务器端脚本,ServerTest

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ServerControler
{
    public class ServerTest
    {
        List<Socket> clientList;//客户端集合
        Socket socket;
        string ip = "127.0.0.1";
        int port = 12345;
        /// <summary>
        /// 构造函数
        /// </summary>
        public ServerTest()
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientList = new List<Socket>();//在构造函数中初始化客户端的集合
        }
        public void StartServer()
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip), port);//把IP和端口组合成IPEndPoint的结构
            socket.Bind(endPoint);//绑定IP和端口
            socket.Listen(10);//设置监听
            Thread therd = new Thread(Accept);//开启新线程
            therd.IsBackground = true;
            therd.Start();
            Console.WriteLine("服务器启动成功");
        }
        /// <summary>
        /// 接收客户端的方法,会挂起当前线程
        /// </summary>
        void Accept()
        {
            Socket client = socket.Accept();//设置连接
            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
            Console.WriteLine(point.Address + "[" + point.Port + "]连接成功");
            clientList.Add(client);//连接进来一个客户端就添加到集合里,然后在下面遍历哪个客户端发的消息,再把消息分别发到其他客户端
            //会挂起当前线程,所以新开一个线程
            Thread thread = new Thread(Receive);
            thread.IsBackground = true;
            thread.Start(client);
            Accept();//尾递归
        }
        /// <summary>
        /// 服务器接收客户端的消息
        /// </summary>
        /// <param name="obj">线程的参数类型只能是Object类型</param>
        void Receive(object obj)//线程的参数类型只能是Object类型
        {
            Socket client = obj as Socket;//把Object强制转换成Socket
            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
            try
            {
                byte[] msg = new byte[1024];
                int msgLenth = client.Receive(msg);
                string clientMsg = point.Address + "[" + point.Port + "] 说: " + Encoding.UTF8.GetString(msg, 0, msgLenth);//获得哪个客户端发过来的消息,进行输出
                Console.WriteLine(clientMsg);
                GuangBo(client, clientMsg);//调用广播这个方法,分发给其他客户端,有人说话了
                //client.Send(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(msg, 0, msgLenth)+" (这是服务器返回的消息)"));//服务器往客户端发送消息
                Receive(client);//尾递归进行等待下一个消息
            }
            catch
            {
                Console.WriteLine(point.Address + "[" + point.Port + "] : " + "断开连接");
                clientList.Remove(client);//如果某个客户端退出链接,就从集合里移除
            }
        }
        /// <summary>
        /// 遍历哪个客户端发的消息,然后把消息向其他客户端广播出去,形成聊天室
        /// </summary>
        /// <param name="client">发消息进来的客户端</param>
        /// <param name="msg">某个客户端发过来的消息</param>
        private void GuangBo(Socket clientOther, string msg)
        {
            foreach (var client in clientList)
            {
                if (client == clientOther)
                {
                    //消息是由这个客户端发过来的,不需要有任何响应
                }
                else
                {
                    client.Send(Encoding.UTF8.GetBytes(msg));//执行Send方法,把消息分发给其他客户端
                }
            }
        }
    }
}

服务器端主函数脚本,Program

using System;
namespace ServerControler
{
    class Program
    {
        static void Main(string[] args)
        {
            ServerTest server = new ServerTest();
            server.StartServer();//执行StartServer方法,开启服务器
            Console.ReadKey();
        }
    }
}

再创建一个ClientController的解决方案,使用一下脚本:

客户端脚本,Client

using System;
using System.Collections.Generic;
usingSystem.Net.Sockets;
using System.Text;
using System.Threading;
namespace ClientController
{
    public class Client
    {
        private Socket ClientSocket;
        public Client()
        {
            ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        public void Connect(string ip, int port)
        {
            ClientSocket.Connect(ip, port);
            Console.WriteLine("连接上服务器");


            Thread thread = new Thread(Receive);
            thread.IsBackground = true;
            thread.Start();
        }
        void Receive()
        {
            while (true)
            {
                try
                {
                    byte[] msg = new byte[1024];
                    int msgLenth = ClientSocket.Receive(msg);//ClientSocket.Receive接收消息,返回接受到得长度
                    Console.WriteLine(Encoding.UTF8.GetString(msg, 0, msgLenth));
                }
                catch
                {
                    Console.WriteLine("服务器拒绝连接,请重新尝试");
                    break;
                }
            }
        }
        public void Send()
        {
            Thread thread = new Thread(ReadAndSend);
            //thread.IsBackground = true;//此处教程视频里注掉了,说是因为在主函数里写了Console.ReadKey(),主线程会把这个线程给关掉,注掉之后就不会影响这个线程,个人测试下来,不发布还是没问题的,但是发布这个程序之后,会发送不了消息,直接退出,所以还是注掉
            thread.Start();//开启线程
        }
        void ReadAndSend()
        {
            Console.WriteLine("请输入要发送的内容,不输入直接按下Enter则退出");
            string msg = Console.ReadLine();//获取输入的内容
            while (msg != "")
            {
                ClientSocket.Send(Encoding.UTF8.GetBytes(msg));//向服务器发送消息
                msg = Console.ReadLine();
            }
        }
    }
}

客户端端主函数脚本,Program

using System;
namespace ClientController
{
    class Program
    {
        static void Main(string[] args)
        {
            Client client = new Client();
            client.Connect("127.0.0.1", 12345);
            client.Send();//执行Send方法,启动线程发送消息
            Console.ReadKey();
        }
    }
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!