c#的Socket实现多人聊天室(附源码)

在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据

套接字之间的连接过程可以分为三个步骤 :
(1)服务器监听。
(2)客户端请求。
(3)连接确认 。

c#的Socket通讯实现聊天室,支持消息与图片发送
c#实现的主要步骤:
1:第一步创建一个开始监听的Socket
2:第二步创建Ip地址和端口号对象
3:第三步让监听的Socket绑定Ip地址跟端口号
4:第四步设置监听队列
5:最后开启一个线程循环监听连接的客户端

聊天室服务器主要起到中转的作用:
当有一个 客服端发送信息或者图片过来的时候,将其发送给所有连接上的客服端

客户端将消息解析成byte数组,在byte数组第一位加上一个数用来表示发送的消息是什么类型的该怎么去解析它(目前支持:文字0,图片1),可以理解成一个简单的协议,双方之间约定好怎么封装,怎么解析内容

界面展示

![在这里插入图片描述](https://img-blog.csdnimg.cn/2021010212410134.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjU0MjM2,size_16,color_FFFFFF,t_70

代码部分展示

服务端:

  public partial class ServerSocket : Form
    {
        public ServerSocket()
        {
            InitializeComponent();
        }
        //启动服务器
        private void bt_start_Click(object sender, EventArgs e)
        {
            try
            {
                //第一步创建一个开始监听的Socket
                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //第二步创建Ip地址和端口号对象
                IPAddress ip = IPAddress.Any;
                IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txt_Point.Text));
                //第三步让监听的Socket绑定Ip地址跟端口号
                socketWatch.Bind(point);
                //设置监听队列
                socketWatch.Listen(int.Parse(this.tb_num.Text));

                Thread t = new Thread(Listen);
                t.IsBackground = true;
                t.Start(socketWatch);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //循环监听连接的客户端
        Socket socketSend;
        void Listen(object o)
        {
            Socket socketWatch = o as Socket;
            while (true)
            {
                //负责监听的Socket来接收客户端的连接,创建跟客户通讯的Socket
                socketSend = socketWatch.Accept();
                //将远程的连接客服端存储字典集合
                SocketHelper.Intanter.dicScoket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
                //添加地址到下拉框与list集合中去
                AddCbItem(socketSend.RemoteEndPoint.ToString());
                AddContent(socketSend.RemoteEndPoint.ToString() + "连接成功");
                Thread t = new Thread(Recive);
                t.IsBackground = true;
                t.Start(socketSend);
            }
        }

        //循环接收发送过来的数据
        void Recive(object o)
        {
            Socket socketSend = o as Socket;
            while (true)
            {
                try
                {
                    string str = "";
                    //客户端连接成功后,服务器接收客服端发送过来的数据
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //实际接收的有效字节
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (buffer[0] == 0)
                    {
                        str = socketSend.RemoteEndPoint.ToString() + ":\r\n" + "   " + Encoding.UTF8.GetString(SocketHelper.Intanter.RemoveFbyte(buffer), 0, r);
                      
                    }
                    else if (buffer[0] == 1)
                    {
                        str = socketSend.RemoteEndPoint.ToString() + ":\r\n" + "   " + SocketHelper.Intanter.ShowImgByByte(SocketHelper.Intanter.RemoveFbyte(buffer));
                      
                        SocketHelper.Intanter.SendMessage(buffer);
                    }
                    byte[] newbuffer = SocketHelper.Intanter.SendMessageToClient(str);
                    SocketHelper.Intanter.SendMessage(newbuffer);
                    AddContent(socketSend.RemoteEndPoint.ToString() + ":" + str);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

            }
        }

        //将连接的IP添加到下拉框
        public void AddCbItem(string ItemName)
        {
            this.BeginInvoke(new MethodInvoker(delegate
            {
                cb_IP.Items.Add(ItemName);
                SocketHelper.Intanter.IPItem.Add(ItemName);
            }));
        }

        //将接受到的内容显示出来
        private void AddContent(string content)
        {
            this.BeginInvoke(new MethodInvoker(delegate
            {
                rtb_log.AppendText(content + " ");
                rtb_log.AppendText("\r\n");
                rtb_log.Focus();//先获取焦点
                rtb_log.Select(rtb_log.TextLength, 0);//选中数据末尾0个字符
                rtb_log.ScrollToCaret();//将滚动条移动到当前位置
                                        //记录收到的字符个数
                                        //toolStripStatusLabel1.Text += (int.Parse(toolStripStatusLabel1.Text) + content.Length).ToString();
            }));
        }

        //弹出客户端
        private void bt_Open_Click(object sender, EventArgs e)
        {
            new ClientSocket().Show();
        }

        //选择连接的客户端发送数据
        private void bt_send_Click(object sender, EventArgs e)
        {
            string msg = this.rtb_sendmsg.Text.Trim();
            //byte[] buffer = Encoding.UTF8.GetBytes(msg);
            获得用户在下拉框的IP地址
            //string ip = this.cb_IP.Text;
            //SocketHelper.Intanter.dicScoket[ip].Send(buffer);
            //socketSend.Send(buffer);
            socketSend.Send(SocketHelper.Intanter.SendMessageToClient(msg, SocketHelper.MessageType.news));
        }




    }

客户端:

  public partial class ClientSocket : Form
    {
        public ClientSocket()
        {
            InitializeComponent();
        }
        Socket socketSend;
        string ImageFile;
        private void bt_start_Click(object sender, EventArgs e)
        {
            try
            {
                //创建负责通讯的Socket
                //第一步创建一个开始监听的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //第二步创建Ip地址和端口号对象
                IPAddress ip = IPAddress.Parse(this.tb_IP.Text);
                IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.tb_Point.Text));
                socketSend.Connect(point);//通过ip 端口号定位一个要连接的服务器端

                Thread t = new Thread(Recive);
                t.IsBackground = true;
                t.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }

        }

        //将接受到的内容显示出来
        private void AddContent(string content)
        {
            this.BeginInvoke(new MethodInvoker(delegate
            {
                rtb_log.AppendText(content + " ");
                rtb_log.AppendText("\r\n");
                rtb_log.Focus();//先获取焦点
                rtb_log.Select(rtb_log.TextLength, 0);//选中数据末尾0个字符
                rtb_log.ScrollToCaret();//将滚动条移动到当前位置
                                        //记录收到的字符个数
                                        //toolStripStatusLabel1.Text += (int.Parse(toolStripStatusLabel1.Text) + content.Length).ToString();
            }));
        }
        void Recive()
        {
            while (true)
            {
                try
                {
                    //客户端连接成功后,服务器接收客服端发送过来的数据

                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //实际接收的有效字节
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (buffer[0] == 0)
                    {
                        string str = Encoding.UTF8.GetString(SocketHelper.Intanter.RemoveFbyte(buffer), 0, r - 1);
                        AddContent(str);
                    }
                    else if (buffer[0] == 1)
                    {
                        string ImageName = SocketHelper.Intanter.ShowImgByByte(SocketHelper.Intanter.RemoveFbyte(buffer));
                        AddCbItem(ImageName);
                    }

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }
        //将接收的图片放到listview里面
        public void AddCbItem(string ItemName)
        {
            this.BeginInvoke(new MethodInvoker(delegate
            {
                ltb_Image.Items.Add(ItemName);
                SocketHelper.Intanter.IPItem.Add(ItemName);
            }));
        }
        private void bt_send_Click(object sender, EventArgs e)
        {
            try
            {
                string msg = $"  " + this.tb_name.Text + " :" + this.rtb_sendmsg.Text.Trim();
                socketSend.Send(SocketHelper.Intanter.SendMessageToClient(msg, SocketHelper.MessageType.news));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

        }

        private void bt_OpeImage_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            DialogResult result = fileDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                this.pt_Image.Image = Image.FromFile(fileDialog.FileName);
                ImageFile = fileDialog.FileName;
            }
        }
        private void bt_SendImage_Click(object sender, EventArgs e)
        {
            if (ImageFile != null && ImageFile != "")
            {

                socketSend.Send(SocketHelper.Intanter.SendMessageToClient(ImageFile, SocketHelper.MessageType.picture));
            }
            else
            {
                MessageBox.Show("未选择图片");
            }

        }

        private void ltb_Image_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.ltb_Image.SelectedItem == null)
            {
                return;
            }
            string FileName = this.ltb_Image.SelectedItem.ToString();

            this.pt_Image.Image = Image.FromFile(Environment.CurrentDirectory + "\\Images\\" + FileName);// Image.FromFile(fileDialog.FileName);


        }
    }

SocketHelper类

 public class SocketHelper
    {
        //将远程连接过来的客服端的IP地址和Socket存入集合
        public static SocketHelper Intanter = new SocketHelper();
        public Dictionary<string, Socket> dicScoket = new Dictionary<string, Socket>();
        public List<string> IPItem = new List<string>();

        //消息类型枚举
        public enum MessageType
        {
            news,
            picture
        }

        /// <summary>
        ///  服务端接收数据后将数据发送给所有的客户端
        /// </summary>
        /// <param name="buffer">发送的消息字节</param>
        /// <param name="ms">消息类型</param>
        /// <returns></returns>
        public void SendMessage(byte[] buffer)
        {

            //获得用户在下拉框的IP地址
            var task1 = new Task(() =>
            {
                for (int i = 0; i < IPItem.Count; i++)
                {
                    string ip = IPItem[i].ToString();
                    dicScoket[ip].Send(buffer.ToArray());
                }
            });
            task1.Start();
        }
        /// <summary>
        ///  将消息转换成消息协议格式
        /// </summary>
        /// <param name="buffer">发送的消息字节</param>
        /// <param name="ms">消息类型</param>
        /// <returns></returns>
        public byte[] SendMessageToClient(string message, MessageType ms = MessageType.news)
        {
            List<byte> newbuffer = new List<byte>();
            byte[] buffer = new byte[0];
            switch (ms)
            {
                case MessageType.news:
                    newbuffer.Add(0);
                    buffer = Encoding.UTF8.GetBytes(message);
                    break;
                case MessageType.picture:
                    newbuffer.Add(1);
                    buffer = SaveImage(message);
                    break;
                default:
                    break;
            }
            newbuffer.AddRange(buffer);
            return newbuffer.ToArray();
        }
        public byte[] RemoveFbyte(byte[] buffer)
        {
            List<byte> newbuffer = buffer.ToList();
            newbuffer.RemoveAt(0);
            return newbuffer.ToArray();
        }
        /// <summary>
        /// 将图片以二进制流
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public byte[] SaveImage(String path)
        {
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); //将图片以文件流的形式进行保存
            BinaryReader br = new BinaryReader(fs);
            byte[] imgBytesIn = br.ReadBytes((int)fs.Length); //将流读入到字节数组中
            return imgBytesIn;
        }
        /// <summary>
        /// 现实二进制流代表的图片
        /// </summary>
        /// <param name="imgBytesIn"></param>
        public string ShowImgByByte(byte[] imgBytesIn)
        {
            lock (this)
            {
                string NewImageName = DateTime.Now.ToString("yyyy-mm-dd hh-mm-ss") + ".jpg";//ImageName(CenterId);//获得图片的名字
                string ImagePath = Environment.CurrentDirectory + $"\\Images\\";//@"F:/AQPXImageURL/" + NewImageName.ToString() + ".jpg";
                if (!Directory.Exists(ImagePath))
                {
                    DirectoryInfo directoryInfo = new DirectoryInfo(ImagePath);
                    directoryInfo.Create();
                }
                ImagePath += NewImageName.ToString();
                if (File.Exists(ImagePath))
                {
                    return NewImageName;
                }
                MemoryStream ms = new MemoryStream(imgBytesIn);
                Bitmap bmp = new Bitmap(ms);
                bmp.Save(ImagePath, ImageFormat.Bmp);
                ms.Close();
                return NewImageName;
            }
            //return NewImageName;

            //pictureBox1.Image = Image.FromStream(ms);
        }

    }

最后如果感兴趣的可以试着将代码进行优化升级,如:实现各种类型的文件发送(将消息头经过你自己个人的封装解析),希望能帮助到大家对socket的学习
附源码:c#socket通讯源码

  • 19
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
namespace ServerDemo { partial class ServerDemo { /// /// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资。 /// /// 如果应释放托管资,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows 窗体设计器生成的代码 /// /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { this.lb_ServerInfo = new System.Windows.Forms.ListBox(); this.bn_Resume = new System.Windows.Forms.Button(); this.bn_Stop = new System.Windows.Forms.Button(); this.bn_Start = new System.Windows.Forms.Button(); this.listBox1 = new System.Windows.Forms.ListBox(); this.cmbClient = new System.Windows.Forms.ComboBox(); this.btnSendto = new System.Windows.Forms.Button(); this.labClientCount = new System.Windows.Forms.Label(); this.SuspendLayout(); // // lb_ServerInfo // this.lb_ServerInfo.FormattingEnabled = true; this.lb_ServerInfo.ItemHeight = 12; this.lb_ServerInfo.Location = new System.Drawing.Point(14, 32); this.lb_ServerInfo.Name = "lb_ServerInfo"; this.lb_ServerInfo.Size = new System.Drawing.Size(572, 100); this.lb_ServerInfo.TabIndex = 61; // // bn_Resume // this.bn_Resume.Location = new System.Drawing.Point(174, 3); this.bn_Resume.Name = "bn_Resume"; this.bn_Resume.Size = new System.Drawing.Size(97, 23); this.bn_Resume.Ta
实现C#的简易聊天,可以使用Socket来进行操作。首先,声明一个套接字(Socket)作为监听套接字,使用AddressFamily.InterNetwork、SocketType.Stream和ProtocolType.Tcp来创建这个套接字。接下来,需要设置服务器的IP地址和端口号。然后,使用Socket的Bind方法将套接字绑定到指定的IP地址和端口上。接着,使用Socket的Listen方法开始监听连接请求。当有客户端连接请求时,可以使用Accept方法接受连接,并创建一个新的套接字来处理该连接。这样就可以与客户端进行通信了。 在聊天中,可以使用多线程来处理多个客户端的连接。当有新的客户端连接时,创建一个新的线程来处理该连接,这样就可以同时处理多个客户端的消息收发。 在处理客户端消息的过程中,可以使用Socket的Receive和Send方法来接收和发送数据。可以使用StreamReader和StreamWriter类来简化数据的读取和写入操作。 通过这些步骤,就可以实现一个简单的C# Socket聊天。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C#基于Socket的简单聊天实践](https://blog.csdn.net/wyqlxy/article/details/46923611)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值