.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

这篇具有很好参考价值的文章主要介绍了.NET编程——利用C#实现基于Socket类的聊天室(WinForm)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在学习C#和MySQL实现注册登录和TCP协议的Socket通信后,本文将介绍如何利用Socket类中的异步通信函数来实现本地聊天室功能,

引言

Socket通信限制了客户端与客户端之间的通信,客户端只能接收来自服务器的消息而不能接收到客户端发送的消息,因此服务器最佳的选择是起到一个中转的作用,A客户端发送消息给服务器,服务器将接收到的A客户端发来的消息发送给B客户端,当然也可以是某部分客户端或者全部客户端,所有的客户端都要和这个服务器建立连接,服务器和客户端都只有两个功能——发送接收消息。

数据库设计

具体操作步骤可参考.NET编程——利用C#和MySQL实现注册登录(WinForm)中的数据库设计部分,这一部分的主要目的在于创建一个本地数据库用于写入和读取账号、密码与在线状态。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

登录注册

窗体设计

可输入账号密码,点击按键即可注册、登录。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

控件选择

根据窗体设计选择相应的控件,包含按键、文本框以及标签等。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)
其中TableLayoutPanel控件用于调整窗体控件的布局,具体可看C#控件自适应布局。

程序设计

step1-连接数据库

static string connStr = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Code\WeTalk\WeTalk\WeTalkDataBase.mdf;Integrated Security=True";  //连接数据库标识
using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
{
    conn.Open();  //打开数据库连接
    /* 执行内容 */
    conn.Close();  //关闭数据库连接
}

step2-注册

/* 点击注册按键时触发 */
private void btnSign_Click(object sender, EventArgs e)
{
    /* 获取控件里的值 */
    string account = txtAccount.Text;
    string password = txtPassword.Text;
    if (account == "" || password == "")
    {
        MessageBox.Show("请输入完整");
        clearText();  //清空文本
        return;
    }
    try
    {
        string sqlSelect = string.Format("select count(*) from [User] where Account='{0}'", account);  //SQL语句,选择表User中Account为account的行
        /// 创建对象时使用using可以在使用完该对象后,自动释放资源
        using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
        {
            using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
            {
                conn.Open();  //打开数据库连接
                if ((int)cmdSelect.ExecuteScalar() > 0)
                {
                    conn.Close();  //关闭数据库连接
                    MessageBox.Show("用户已存在!");
                    clearText();  //清空文本
                }
                else
                {
                    if (account == "admin" && password != "admin")
                    {
                        conn.Close();  //关闭数据库连接
                        MessageBox.Show("注册失败");
                        clearText();  //清空文本
                        return;
                    }
                    string sqlInsert = string.Format("insert into [User] (Account,Password,IsOnline)values('{0}','{1}',{2})",account, password, 0);//SQL语句,将参数Name、Password、IsOnline插入到表User中
                    SqlCommand cmdInsert = new SqlCommand(sqlInsert, conn);
                    cmdInsert.ExecuteNonQuery();  //执行SQL语句
                    conn.Close();  //关闭数据库连接
                    MessageBox.Show("注册成功");
                    clearText();  //清空文本
                }
            }
        }
    }
    catch (Exception ex)  //异常捕获
    {
        MessageBox.Show(ex.ToString());  //显示异常信息
    }
}

step3-登录

/* 点击登录按键时触发 */
private void btnLogin_Click(object sender, EventArgs e)
{
    /* 获取控件里的值 */
    string account = txtAccount.Text;
    string password = txtPassword.Text;
    if (account == "" || password == "")
    {
        MessageBox.Show("请输入完整!");
        clearText();  //清空文本
        return;
    }
    string sqlSelect = string.Format("select Password,IsOnline from [User] where Account='{0}'", account);  //SQL语句,选择PassWord和IsOnline
    /* 创建对象时使用using可以在使用完该对象后,自动释放资源 */
    using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
    {
        using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
        {
            conn.Open();  //打开数据库连接
            SqlDataReader sqlRead = cmdSelect.ExecuteReader(); //遍历数据库
            //cmdSelect.ExecuteScalar();  //执行SQL语句
            if (!sqlRead.Read())
            {
                conn.Close();  //关闭数据库连接
                MessageBox.Show("账号不存在!请重新输入");
                clearText();  //清空文本
            }
            else if (sqlRead["IsOnline"].ToString().Trim() == "1" && account != "admin")
            {
                conn.Close();  //关闭数据库连接
                MessageBox.Show("账号已登录!请重新输入");
                clearText();  //清空文本
            }
            else if (sqlRead["Password"].ToString().Trim() == password || (account == "admin" && password == "admin"))
            {
                conn.Close();  //关闭数据库连接
                MessageBox.Show("登录成功!");
                string sqlUpdate = string.Format("update [User] set IsOnline={0} where Account='{1}'", 1, account);  //SQL语句,更新IsOnline为上线状态
                using (SqlCommand cmdUpdate = new SqlCommand(sqlUpdate, conn))  //创建数据库命令类
                {
                    conn.Open();  //打开数据库连接
                    //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                    cmdUpdate.ExecuteNonQuery();  //执行SQL语句
                    conn.Close();  //关闭数据库连接
                    clearText();  //清空文本
                    FormTalk formTalk = new FormTalk(account, this);  //跨窗体传入参数
                    this.Hide();  //隐藏当前窗体
                    formTalk.ShowDialog();  //显示聊天窗体
                }
            }
            else
            {
                conn.Close();  //关闭数据库连接
                MessageBox.Show("密码错误!请重新输入");
                clearText();
            }
        }
    }
}

源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Client
{
    public partial class FormLogin : Form
    {
        // 连接数据库标示
        const string connStr = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Code\WeTalk\WeTalk\WeTalkDataBase.mdf;Integrated Security=True";  //连接数据库标识
        public FormLogin()
        {
            InitializeComponent();
        }
        /* 打开窗体时触发 */
        private void FormLogin_Load(object sender, EventArgs e)
        {
            clearText();
            //clearDataBase();
        }
        /* 清空数据库函数 */
        private void clearDataBase()
        {
            string sqlDelete = "delete from [User]";  //SQL语句,清空表User内容
            using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
            {
                using (SqlCommand cmdDelete = new SqlCommand(sqlDelete, conn)) //创建数据库命令类
                {
                    conn.Open();  //打开数据库连接
                    //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                    cmdDelete.ExecuteNonQuery();  //执行SQL语句
                    conn.Close();  //关闭数据库连接
                    MessageBox.Show("管理员重置了所有账号");
                }
            }
        }
        /* 清空输入框内容函数 */
        private void clearText()
        {
            /* Action委托修改控件内容 */
            this.Invoke(new Action(() =>
            {
                txtAccount.Text = "";
                txtPassword.Text = "";
            }));
        }
        /* 点击注册按键时触发 */
        private void btnSign_Click(object sender, EventArgs e)
        {
            /* 获取控件里的值 */
            string account = txtAccount.Text;
            string password = txtPassword.Text;
            if (account == "" || password == "")
            {
                MessageBox.Show("请输入完整");
                clearText();  //清空文本
                return;
            }
            try
            {
                string sqlSelect = string.Format("select count(*) from [User] where Account='{0}'", account);  //SQL语句,选择表User中Account为account的行
                /// 创建对象时使用using可以在使用完该对象后,自动释放资源
                using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
                {
                    using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
                    {
                        conn.Open();  //打开数据库连接
                        if ((int)cmdSelect.ExecuteScalar() > 0)
                        {
                            conn.Close();  //关闭数据库连接
                            MessageBox.Show("用户已存在!");
                            clearText();  //清空文本
                        }
                        else
                        {
                            if (account == "admin" && password != "admin")
                            {
                                conn.Close();  //关闭数据库连接
                                MessageBox.Show("注册失败");
                                clearText();  //清空文本
                                return;
                            }
                            string sqlInsert = string.Format("insert into [User] (Account,Password,IsOnline)values('{0}','{1}',{2})",account, password, 0);
                            //SQL语句,将参数Name、Password、IsOnline插入到表User中
                            SqlCommand cmdInsert = new SqlCommand(sqlInsert, conn);
                            cmdInsert.ExecuteNonQuery();  //执行SQL语句
                            conn.Close();  //关闭数据库连接
                            MessageBox.Show("注册成功");
                            clearText();  //清空文本
                        }
                    }
                }
            }
            catch (Exception ex)  //异常捕获
            {
                MessageBox.Show(ex.ToString());  //显示异常信息
            }
        }
        /* 点击登录按键时触发 */
        private void btnLogin_Click(object sender, EventArgs e)
        {
            /* 获取控件里的值 */
            string account = txtAccount.Text;
            string password = txtPassword.Text;
            if (account == "" || password == "")
            {
                MessageBox.Show("请输入完整!");
                clearText();  //清空文本
                return;
            }
            string sqlSelect = string.Format("select Password,IsOnline from [User] where Account='{0}'", account);  //SQL语句,选择PassWord和IsOnline
            /* 创建对象时使用using可以在使用完该对象后,自动释放资源 */
            using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
            {
                using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
                {
                    conn.Open();  //打开数据库连接
                    SqlDataReader sqlRead = cmdSelect.ExecuteReader(); //遍历数据库
                    //cmdSelect.ExecuteScalar();  //执行SQL语句
                    if (!sqlRead.Read())
                    {
                        conn.Close();  //关闭数据库连接
                        MessageBox.Show("账号不存在!请重新输入");
                        clearText();  //清空文本
                    }
                    else if (sqlRead["IsOnline"].ToString().Trim() == "1" && account != "admin")
                    {
                        conn.Close();  //关闭数据库连接
                        MessageBox.Show("账号已登录!请重新输入");
                        clearText();  //清空文本
                    }
                    else if (sqlRead["Password"].ToString().Trim() == password || (account == "admin" && password == "admin"))
                    {
                        conn.Close();  //关闭数据库连接
                        MessageBox.Show("登录成功!");
                        string sqlUpdate = string.Format("update [User] set IsOnline={0} where Account='{1}'", 1, account);  //SQL语句,更新IsOnline为上线状态
                        using (SqlCommand cmdUpdate = new SqlCommand(sqlUpdate, conn))  //创建数据库命令类
                        {
                            conn.Open();  //打开数据库连接
                            //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                            cmdUpdate.ExecuteNonQuery();  //执行SQL语句
                            conn.Close();  //关闭数据库连接
                            clearText();  //清空文本
                            FormTalk formTalk = new FormTalk(account, this);  //跨窗体传入参数
                            this.Hide();  //隐藏当前窗体
                            formTalk.ShowDialog();  //显示聊天窗体
                        }
                    }
                    else
                    {
                        conn.Close();  //关闭数据库连接
                        MessageBox.Show("密码错误!请重新输入");
                        clearText();
                    }
                }
            }
        }
    }
}

创建聊天室(服务器)

服务器起中转作用,无需输入发送的消息,将接受到的消息原封不动的返回给所有客户端即可。

窗体设计

显示服务器的IP与端口号。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

控件选择

根据窗体设计选择文本框控件。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)
其中TableLayoutPanel控件用于调整窗体控件的布局,具体可看C#控件自适应布局。

程序设计

step1-定义变(常)量

#region 变(常)量定义
/*  buffer:存放接受消息缓存的数组
 *  listSocketWorker:存放套接字的列表
 *  MAXVALUE:服务器最大连接数
 *  PORT:服务器的网络终结点,即IP:端口号
 *  socketWatch:监听客户端连接的服务器套接字
 */
byte[] buffer = new byte[1024 * 1024];  //创建接受消息缓存数组并约定缓存长度解决粘包问题
private List<Socket> listSocketWorker = new List<Socket>();  //创建列表存储套接字
const int MAXVALUE = 10;  //设定最大连接数
const int PORT = 8888;  //设定端口号
private Socket socketServer = null;  //创建全局变量服务器套接字
#endregion

step2-创建服务器并监听客户端连接

创建了一个btnOpen_Click()委托函数,后续可以直接或者利用委托触发创建服务器。

/* 窗体打开时触发 */
private void FormOpen_Load(object sender, EventArgs e)
{
    btnOpen_Click(this, null);  //触发打开服务器按键
}
/* 点击打开按键时触发 */
private void btnOpen_Click(object sender, EventArgs e)
{
    socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  //实例化TCP通信协议Socket
    /* 监听客户端 */
    IPAddress _ip = IPAddress.Any;  //监听所有活动网卡
    IPEndPoint _port = new IPEndPoint(_ip, PORT);  //创建监听对象
    try
    {
        socketServer.Bind(_port);  //开始监听
        socketServer.Listen(10);  //服务器最大连接数为10
        socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);  //异步接受连接
        ///参数1:回调函数,参数2:传入回调函数的参数
    }
    catch  //异常捕获
    {
        MessageBox.Show("服务器已上线");
    }
}

step3-接受连接回调

利用Socket类中的BeginAccept()函数开始接受客户端的连接,此时会自动生成一个线程去接受客户端的连接,接受到客户端的连接后返回请求的对象给回调函数。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

/* 接受连接异步回调处理 */
private void AcceptCallback(IAsyncResult ia)
{
    socketServer = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
    Socket socketWorker = socketServer.EndAccept(ia);  //连接成功并返回一个套接字用于通信
    if (socketWorker != null)
    {
        /* Action委托修改lblState控件内容 */
        this.Invoke(new Action(() =>
        {
            lblState.Text = socketWorker.LocalEndPoint.ToString();
        }));
    }
    Receive(socketWorker);
    socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);  //再次异步接受连接
    /// 利用BeginAccept异步接受连接时会自动创建线程并自动结束线程
}

step4-接受消息

为了保证BeginReceive()函数在接受客户端的消息时再次接受到消息时接受消息缓存错误,利用线程锁保证消息被完整转发出去。

private void Receive(Socket socketWorker)
{
    if(!listSocketWorker.Contains(socketWorker))
        listSocketWorker.Add(socketWorker);  //将套接字保存到列表中
    socketWorker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketWorker);  //异步接受消息
    /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为,参数5:回调函数,参数6:传入回调函数的参数
}
/* 接受消息异步回调处理 */
private void ReceiveCallback(IAsyncResult ia)
{
    Socket socketWorker = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
    try
    {
        int bytesRead = socketWorker.EndReceive(ia);  //接受消息成功并返回消息长度
        if (bytesRead > 0)  //接受到非空消息
        {
            string context = Encoding.Default.GetString(buffer, 0, bytesRead);  //缓存解码为字符串
            socketWorker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketWorker);  //再次异步接受消息
            /// 利用BeginReceive异步接受消息时会自动创建线程并自动结束线程
            if (!String.IsNullOrEmpty(context))
            {
                /* 线程锁——完成代码块后执行其他线程 */
                lock (socketWorker)
                {
                    foreach (var item in listSocketWorker)  // 接受消息后消息转发给所有客户端
                        item.Send(buffer, 0, bytesRead, SocketFlags.None);
                }
            }
        }
    }
    catch  //异常捕获
    {
        MessageBox.Show("客户端已下线");
        listSocketWorker.Remove(socketWorker);
    }
}

源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Server
{
    public partial class FormOpen : Form
    {
        #region 变(常)量定义
        /*  buffer:存放接受消息缓存的数组
         *  listSocketWorker:存放套接字的列表
         *  MAXVALUE:服务器最大连接数
         *  PORT:服务器的网络终结点,即IP:端口号
         *  socketWatch:监听客户端连接的服务器套接字
         */
        byte[] buffer = new byte[1024 * 1024];  //创建接受消息缓存数组并约定缓存长度解决粘包问题
        private List<Socket> listSocketWorker = new List<Socket>();  //创建列表存储套接字
        const int MAXVALUE = 10;  //设定最大连接数
        const int PORT = 8888;  //设定端口号
        private Socket socketServer = null;  //创建全局变量服务器套接字
        #endregion
        public FormOpen()
        {
            InitializeComponent();
        }
        /* 窗体打开时触发 */
        private void FormOpen_Load(object sender, EventArgs e)
        {
            btnOpen_Click(this, null);  //触发打开服务器按键
        }
        /* 点击打开按键时触发 */
        private void btnOpen_Click(object sender, EventArgs e)
        {
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  //实例化TCP通信协议Socket
            /* 监听客户端 */
            IPAddress _ip = IPAddress.Any;  //监听所有活动网卡
            IPEndPoint _port = new IPEndPoint(_ip, PORT);  //创建监听对象
            try
            {
                socketServer.Bind(_port);  //开始监听
                socketServer.Listen(10);  //服务器最大连接数为10
                socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);  //异步接受连接
                ///参数1:回调函数,参数2:传入回调函数的参数
            }
            catch  //异常捕获
            {
                MessageBox.Show("服务器已上线");
            }
        }
        /* 接受连接异步回调处理 */
        private void AcceptCallback(IAsyncResult ia)
        {
            socketServer = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
            Socket socketWorker = socketServer.EndAccept(ia);  //连接成功并返回一个套接字用于通信
            if (socketWorker != null)
            {
                /* Action委托修改lblState控件内容 */
                this.Invoke(new Action(() =>
                {
                    lblState.Text = socketWorker.LocalEndPoint.ToString();
                }));
            }
            Receive(socketWorker);
            socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);  //再次异步接受连接
            /// 利用BeginAccept异步接受连接时会自动创建线程并自动结束线程
        }
        private void Receive(Socket socketWorker)
        {
            if(!listSocketWorker.Contains(socketWorker))
                listSocketWorker.Add(socketWorker);  //将套接字保存到列表中
            socketWorker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketWorker);  //异步接受消息
            /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为,参数5:回调函数,参数6:传入回调函数的参数
        }
        /* 接受消息异步回调处理 */
        private void ReceiveCallback(IAsyncResult ia)
        {
            Socket socketWorker = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
            try
            {
                int bytesRead = socketWorker.EndReceive(ia);  //接受消息成功并返回消息长度
                if (bytesRead > 0)  //接受到非空消息
                {
                    string context = Encoding.Default.GetString(buffer, 0, bytesRead);  //缓存解码为字符串
                    socketWorker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketWorker);  //再次异步接受消息
                    /// 利用BeginReceive异步接受消息时会自动创建线程并自动结束线程
                    if (!String.IsNullOrEmpty(context))
                    {
                        /* 线程锁——完成代码块后执行其他线程 */
                        lock (socketWorker)
                        {
                            foreach (var item in listSocketWorker)  // 接受消息后消息转发给所有客户端
                                item.Send(buffer, 0, bytesRead, SocketFlags.None);
                        }
                    }
                }
            }
            catch  //异常捕获
            {
                MessageBox.Show("客户端已下线");
                listSocketWorker.Remove(socketWorker);
            }
        }
        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {

        }
    }
}

创建用户(客户端)

既然是聊天室,当然能显示在线的用户。同时,在退出聊天室时可保存聊天记录。当聊天区内容过多时清空聊天区内。最后,管理员可以重置聊天室用户的账号与密码。

窗体设计

可显示在线用户和聊天内容,点击按键即可发送消息,重置账号与密码。.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

控件选择

根据窗体设计选择相应的控件,包含按键、文本框、标签以及菜单栏等。
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

其中,菜单栏的设置在聊天区内,故此在txtTalk控件属性中的ContextMenuStrip需选择设计好的菜单栏,如下图所示:
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)
.NET编程——利用C#实现基于Socket类的聊天室(WinForm)

程序设计

step1-定义变(常)量

定义_ip时需要根据活跃网卡的IP来设置,端口号与服务器的端口号保持一致即可。

#region 变(常)量定义
/*  _ip:服务器IP
 *  _path:存放聊天记录的路径
 *  buffer:存放接受消息缓存的数组
 *  connStr:连接数据库标示
 *  lastForm:存放上级窗体
 *  PORT:服务器的网络终结点,即IP:端口号
 *  socketClient:与服务器通信的客户端套接字
 *  strAccount:用户名字符串
 *  thUpdateUser:用于更新在线用户的线程
 */
IPAddress _ip = IPAddress.Parse("10.102.101.212");  //IPAddress包含了一个IP地址,IPEndPoin包含了一对IP地址和端口
//IPAddress _ip = IPAddress.Parse("192.168.250.66");  //IPAddress包含了一个IP地址,IPEndPoin包含了一对IP地址和端口
string _path = System.AppDomain.CurrentDomain.BaseDirectory + @"聊天记录\";  //设置聊天记录保存路径
byte[] buffer = new byte[5 * 1024 * 1024];  //创建接受消息缓存数组并约定缓存长度解决粘包问题
const string connStr = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Code\WeTalk\WeTalk\WeTalkDataBase.mdf;Integrated Security=True";  //连接数据库标识
Form lastForm;  //上级窗体
const int PORT = 8888;  //设定端口号
Socket socketClient = null;  //创建全局变量客户端套接字
public string strAccount;  //创建全局变量用户名
Thread thUpdateUser;  //创建线程用于更新在线用户
#endregion

step2-权限设置并重置账户

管理员才可重置账户与密码,通过修改按键的Enabled属性来设置权限。

/* 窗体打开时触发 */
private void FormTalk_Load(object sender, EventArgs e)
{
    btnClean.Enabled = false;
    /* Action委托修改控件内容 */
    this.Invoke(new Action(() =>
    {
        lblAccount.Text = "用户名:" + this.strAccount;  //显示用户名
        /* 权限设置 */
        if (strAccount == "admin")
            btnClean.Enabled = true;
        else
            btnClean.Enabled = false;
    }));
    /*
    	省略…………省略
    */
}
/* 点击重置按键时触发 */
private void btnClear_Click(object sender, EventArgs e)
{
    /* 清空数据库 */
    string sqlDelete = "delete from [User]";  //SQL语句,清空User内容
    using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
    {
        using (SqlCommand cmdDelete = new SqlCommand(sqlDelete, conn)) //创建数据库命令类
        {
            conn.Open();  //打开数据库连接
            //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
            cmdDelete.ExecuteNonQuery();  //执行SQL语句
            conn.Close();  //关闭数据库连接
            ShowMsg("管理员重置了所有账号");
        }
    }
}

step3-更新在线用户

创建一个线程去查询SQL表中在线的用户,用户在线时添加至listBox控件中去,用户不在线时从listBox控件中移除该用户。

/* 窗体打开时触发 */
private void FormTalk_Load(object sender, EventArgs e)
{
    /*
    	省略…………省略
    */
    /* 线程更新在线用户 */
    thUpdateUser = new Thread(UpdateUser);  //实例化线程
    thUpdateUser.IsBackground = true;
    thUpdateUser.Start();  //启动线程
    /*
    	省略…………省略
    */
}
/* 更新在线用户线程函数 */
void UpdateUser()
{
    while (true)  //死循环
    {
        /* 更新在线对象 */
        string sqlSelect = string.Format("select Account,IsOnline from [User]");  //SQL语句,选择表User中Account和IsOnline
        /// 创建对象时使用using可以在使用完该对象后,自动释放资源
        using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
        {
            using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
            {
                conn.Open();  //打开数据库连接
                SqlDataReader sqlRead = cmdSelect.ExecuteReader(); //遍历数据库
                while (sqlRead.Read())
                {
                    int index = lstAccount.FindString(sqlRead["Account"].ToString()); //列表中某值的下标
                    // 列表里没有 但 在线的
                    if (index == -1 && (int)sqlRead["IsOnline"] == 1)
                    {
                        /* Action委托修改lstAccount控件内容 */
                        this.Invoke(new Action(() =>
                        {
                            lstAccount.Items.Add(sqlRead["Account"].ToString());
                        }));
                    }
                    // 列表里有 但 不在线的
                    else if (index != -1 && (int)sqlRead["IsOnline"] == 0)
                    {
                        /* Action委托修改lstAccount控件内容 */
                        this.Invoke(new Action(() =>
                        {
                            lstAccount.Items.RemoveAt(index);
                        }));
                    }
                } 
                conn.Close();  //关闭数据库连接
            }
                }
            }
        }

step4-写入历史聊天记录

/* 窗体打开时触发 */
private void FormTalk_Load(object sender, EventArgs e)
{
    /*
    	省略…………省略
    */
    try
    {
	  	/*
	   		省略…………省略
	   	*/
        /* 写入历史聊天记录 */
        string log_path = _path + strAccount + "_log.txt";
        if (File.Exists(log_path))
        {
            string[] lines = File.ReadAllLines(log_path);
            foreach (string line in lines)
            {
                this.Invoke(new Action(() =>
                {
                    txtTalk.AppendText(line + "\r\n");
                }));
            }
        }
    }
    /*
    	省略…………省略
    */
}

step5-连接服务器

/* 窗体打开时触发 */
private void FormTalk_Load(object sender, EventArgs e)
{
	/*
		省略…………省略
	*/
    try
    {
        socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  //实例化TCP通信协议Socket
        IPEndPoint _port = new IPEndPoint(_ip, PORT); //创建连接对象
        socketClient.SendTimeout = 1000;  //设置连接时限
        socketClient.BeginConnect(_port, new AsyncCallback(ConnectCallback), socketClient);  //异步请求连接
        /// 参数1:服务器终结点,参数2:回调函数,参数3:传入回调函数的参数
	/*
		省略…………省略
	*/
    }
    catch { }  //异常捕获
}
/* 发送连接异步回调处理 */
private void ConnectCallback(IAsyncResult ia)
{
    try
    {
        ((Socket)ia.AsyncState).EndConnect(ia);  //结束连接请求
        socketClient = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
        byte[] sendBuffer = Encoding.Default.GetBytes(strAccount + ":上线了");  //缓存解码为字符串
        socketClient.Send(sendBuffer);  //发送缓存至服务器
        socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketClient);  //异步接受消息
        /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为,参数5:回调函数,参数6:传入回调函数的参数
    }
    catch (Exception ex)  //异常捕获
    {
        ShowMsg(ex.ToString());
    }
} 

step6-接受消息回调

利用Socket类中的BeginReceive()函数接受服务器发来的消息,此时会自动生成一个线程去接受消息,接受到消息后返回请求的对象给回调函数,利用EndReceive()函数去结束接受消息并返回接受缓存长度。同时,利用线程锁保证接收到消息被完整的解码显示在聊天区。

/* 接受消息异步回调处理 */
private void ReceiveCallback(IAsyncResult ia)
{
    socketClient = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
    if (socketClient == null)
    {
        return;
    }
    try
    {
        int bytesRead = socketClient.EndReceive(ia);  //接受消息成功并返回消息长度 
        if (bytesRead > 0)  //接受到非空消息
        {
            string context = Encoding.Default.GetString(buffer, 0, bytesRead);  //缓存解码为字符串
            socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketClient);  //异步接受消息
            /// 利用BeginReceive异步接受消息时会自动创建线程并自动结束线程
            if (!String.IsNullOrEmpty(context))
            {
                /* 线程锁——完成代码块后执行其他线程 */
                lock (socketClient)
                {
                    string[] strContext = context.Split(':');  //解码字符串
                    if (strContext.Length > 0 && strContext[0] != strAccount)
                        ShowMsg(context);
                    /// 服务器转接的消息不是用户自生发出的数据时才显示
                }
            }
        }
    }
    catch(Exception ex)  //异常捕获
    {
        ShowMsg(ex.ToString());
    }
}

step7-发送消息与显示消息

/* 点击发送按键时触发 */
private void btnSend_Click(object sender, EventArgs e)
{
    try
    {
        /* 发送消息至服务器 */
        byte[] buffer = Encoding.Default.GetBytes(strAccount + ":" + txtType.Text);  //将泛型转换为数组
        socketClient.Send(buffer, 0, buffer.Length, SocketFlags.None);  //客户端发送消息至服务器
        /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为
        /* Action委托修改控件内容 */
        this.Invoke(new Action(() =>
        {
            ShowMsg(strAccount + ":" + txtType.Text);
            txtType.Text = "";
        }));
    }
    catch(Exception ex)
    {
        ShowMsg(ex.ToString());
    }
}
/* 显示消息函数 */
private void ShowMsg(string str)
{
    string time = DateTime.Now.ToString("G");  //获取当前时间(长日期/时间)
    /* Action委托修改txtTalk控件内容 */
    this.Invoke(new Action(() =>
    {
        txtTalk.AppendText(time + "\r\n" + str + "\r\n");
    }));
}

step8-清空聊天区

修改主线程中的控件内容必须通过多线程或者委托的方式来实现,本文通过EventHandler委托来触发Action委托修改控件内容,方法如下:

/* 点击右键菜单项时触发 */
private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
    contextMenuStrip.Items[0].Click += new EventHandler(clearTxtTalk);     //定义EvendHandler委托
}
/* 聊天区记录清空函数 */
private void clearTxtTalk(object sender, EventArgs e)
{
	/* Action委托修改txtTalk控件内容 */
    this.Invoke(new Action(() =>
    {
        txtTalk.Text = "";
    }));
}

step9-保存聊天记录

/* 关闭窗体时触发 */
private void FormTalk_FormClosing(object sender, FormClosingEventArgs e)
{
    DialogResult res = MessageBox.Show("是否保存聊天记录?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);  //保存结果信息
    /// 参数1:显示文本,参数2:标题,参数3:按键类型,参数4:显示图标
    if (res == DialogResult.Yes)
    {
        if (!System.IO.Directory.Exists(_path))
            System.IO.Directory.CreateDirectory(_path);
        /* 保存聊天记录 */
        string log_path = _path + strAccount + "_log.txt";
        using (FileStream logFileStream = new FileStream(log_path, FileMode.Create, FileAccess.Write))  //创建写入文件
        {
            using (StreamWriter logWriter = new StreamWriter(logFileStream))
            {
                logWriter.Write(txtTalk.Text);
                logWriter.Close();
            }
            logFileStream.Close();
        }
        e.Cancel = false;  //确认关闭窗体
    }
    else if (res == DialogResult.No)  //不保存聊天记录
    {
        e.Cancel = false;  //确认关闭窗体
    }
    else  //取消
    {
        e.Cancel = true;  //取消关闭窗体
    }
}
/* 关闭窗体后触发 */
private void FormTalk_FormClosed(object sender, FormClosedEventArgs e)
{
    try
    {
        string sqlUpdate = string.Format("update [User] set IsOnline={0} where Account='{1}'", 0, strAccount);  //SQL语句,更新isOnline为上线状态
        using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
        {
            using (SqlCommand cmdUpdate = new SqlCommand(sqlUpdate, conn))  //创建数据库命令类
            {
                conn.Open();  //打开数据库连接
                //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                cmdUpdate.ExecuteNonQuery();  //执行SQL语句
                conn.Close();  //关闭数据库连接
                System.Environment.Exit(0);  //杀死进程
            }
        }
    }
    catch { }  //异常捕获
}

源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Client
{
    public partial class FormTalk : Form
    {
        #region 变(常)量定义
        /*  _ip:服务器IP
         *  _path:存放聊天记录的路径
         *  buffer:存放接受消息缓存的数组
         *  connStr:连接数据库标示
         *  lastForm:存放上级窗体
         *  PORT:服务器的网络终结点,即IP:端口号
         *  socketClient:与服务器通信的客户端套接字
         *  strAccount:用户名字符串
         *  thUpdateUser:用于更新在线用户的线程
         */
        IPAddress _ip = IPAddress.Parse("10.102.101.212");  //IPAddress包含了一个IP地址,IPEndPoin包含了一对IP地址和端口
        //IPAddress _ip = IPAddress.Parse("192.168.250.66");  //IPAddress包含了一个IP地址,IPEndPoin包含了一对IP地址和端口
        string _path = System.AppDomain.CurrentDomain.BaseDirectory + @"聊天记录\";  //设置聊天记录保存路径
        byte[] buffer = new byte[5 * 1024 * 1024];  //创建接受消息缓存数组并约定缓存长度解决粘包问题
        const string connStr = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Code\WeTalk\WeTalk\WeTalkDataBase.mdf;Integrated Security=True";  //连接数据库标识
        Form lastForm;  //上级窗体
        const int PORT = 8888;  //设定端口号
        Socket socketClient = null;  //创建全局变量客户端套接字
        public string strAccount;  //创建全局变量用户名
        Thread thUpdateUser;  //创建线程用于更新在线用户
        #endregion
        public FormTalk(string str,Form form)
        {
            this.strAccount = str;  //窗体间传值
            this.lastForm = form;
            InitializeComponent();
        }
        /* 更新在线用户线程函数 */
        void UpdateUser()
        {
            while (true)  //死循环
            {
                /* 更新在线对象 */
                string sqlSelect = string.Format("select Account,IsOnline from [User]");  //SQL语句,选择表User中Account和IsOnline
                /// 创建对象时使用using可以在使用完该对象后,自动释放资源
                using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
                {
                    using (SqlCommand cmdSelect = new SqlCommand(sqlSelect, conn))  //创建数据库命令类
                    {
                        conn.Open();  //打开数据库连接
                        SqlDataReader sqlRead = cmdSelect.ExecuteReader(); //遍历数据库
                        while (sqlRead.Read())
                        {
                            int index = lstAccount.FindString(sqlRead["Account"].ToString()); //列表中某值的下标
                            // 列表里没有 但 在线的
                            if (index == -1 && (int)sqlRead["IsOnline"] == 1)
                            {
                                /* Action委托修改lstAccount控件内容 */
                                this.Invoke(new Action(() =>
                                {
                                    lstAccount.Items.Add(sqlRead["Account"].ToString());
                                }));
                            }
                            // 列表里有 但 不在线的
                            else if (index != -1 && (int)sqlRead["IsOnline"] == 0)
                            {
                                /* Action委托修改lstAccount控件内容 */
                                this.Invoke(new Action(() =>
                                {
                                    lstAccount.Items.RemoveAt(index);
                                }));
                            }
                        } 
                        conn.Close();  //关闭数据库连接
                    }
                }
            }
        }
        /* 窗体打开时触发 */
        private void FormTalk_Load(object sender, EventArgs e)
        {
            btnClean.Enabled = false;
            /* Action委托修改控件内容 */
            this.Invoke(new Action(() =>
            {
                lblAccount.Text = "用户名:" + this.strAccount;  //显示用户名
                /* 权限设置 */
                if (strAccount == "admin")
                    btnClean.Enabled = true;
                else
                    btnClean.Enabled = false;
            }));
            /* 线程更新在线用户 */
            thUpdateUser = new Thread(UpdateUser);  //实例化线程
            thUpdateUser.IsBackground = true;
            thUpdateUser.Start();  //启动线程
            try
            {
                socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  //实例化TCP通信协议Socket
                IPEndPoint _port = new IPEndPoint(_ip, PORT); //创建连接对象
                socketClient.SendTimeout = 1000;  //设置连接时限
                socketClient.BeginConnect(_port, new AsyncCallback(ConnectCallback), socketClient);  //异步请求连接
                /// 参数1:服务器终结点,参数2:回调函数,参数3:传入回调函数的参数
                /* 写入历史聊天记录 */
                string log_path = _path + strAccount + "_log.txt";
                if (File.Exists(log_path))
                {
                    string[] lines = File.ReadAllLines(log_path);
                    foreach (string line in lines)
                    {
                        this.Invoke(new Action(() =>
                        {
                            txtTalk.AppendText(line + "\r\n");
                        }));
                    }
                }
            }
            catch { }  //异常捕获
        }
        /* 发送连接异步回调处理 */
        private void ConnectCallback(IAsyncResult ia)
        {
            try
            {
                ((Socket)ia.AsyncState).EndConnect(ia);  //结束连接请求
                socketClient = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
                byte[] sendBuffer = Encoding.Default.GetBytes(strAccount + ":上线了");  //缓存解码为字符串
                socketClient.Send(sendBuffer);  //发送缓存至服务器
                socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketClient);  //异步接受消息
                /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为,参数5:回调函数,参数6:传入回调函数的参数
            }
            catch (Exception ex)  //异常捕获
            {
                ShowMsg(ex.ToString());
            }
        } 
        /* 显示消息函数 */
        private void ShowMsg(string str)
        {
            string time = DateTime.Now.ToString("G");  //获取当前时间(长日期/时间)
            /* Action委托修改txtTalk控件内容 */
            this.Invoke(new Action(() =>
            {
                txtTalk.AppendText(time + "\r\n" + str + "\r\n");
            }));
        }
        /* 接受消息异步回调处理 */
        private void ReceiveCallback(IAsyncResult ia)
        {
            socketClient = ia.AsyncState as Socket;  //将传入回调函数的参数实例化为Socket类
            if (socketClient == null)
            {
                return;
            }
            try
            {
                int bytesRead = socketClient.EndReceive(ia);  //接受消息成功并返回消息长度 
                if (bytesRead > 0)  //接受到非空消息
                {
                    string context = Encoding.Default.GetString(buffer, 0, bytesRead);  //缓存解码为字符串
                    socketClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socketClient);  //异步接受消息
                    /// 利用BeginReceive异步接受消息时会自动创建线程并自动结束线程
                    if (!String.IsNullOrEmpty(context))
                    {
                        /* 线程锁——完成代码块后执行其他线程 */
                        lock (socketClient)
                        {
                            string[] strContext = context.Split(':');  //解码字符串
                            if (strContext.Length > 0 && strContext[0] != strAccount)
                                ShowMsg(context);
                            /// 服务器转接的消息不是用户自生发出的数据时才显示
                        }
                    }
                }
            }
            catch(Exception ex)  //异常捕获
            {
                ShowMsg(ex.ToString());
            }
        }
        /* 点击发送按键时触发 */
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                /* 发送消息至服务器 */
                byte[] buffer = Encoding.Default.GetBytes(strAccount + ":" + txtType.Text);  //将泛型转换为数组
                socketClient.Send(buffer, 0, buffer.Length, SocketFlags.None);  //客户端发送消息至服务器
                /// 参数1:接受消息缓存,参数2:接受消息缓存偏倚,参数3:接受消息缓存长度,参数4:套接字行为
                /* Action委托修改控件内容 */
                this.Invoke(new Action(() =>
                {
                    ShowMsg(strAccount + ":" + txtType.Text);
                    txtType.Text = "";
                }));
            }
            catch(Exception ex)
            {
                ShowMsg(ex.ToString());
            }
        }
        /* 点击重置按键时触发 */
        private void btnClear_Click(object sender, EventArgs e)
        {
            /* 清空数据库 */
            string sqlDelete = "delete from [User]";  //SQL语句,清空User内容
            using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
            {
                using (SqlCommand cmdDelete = new SqlCommand(sqlDelete, conn)) //创建数据库命令类
                {
                    conn.Open();  //打开数据库连接
                    //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                    cmdDelete.ExecuteNonQuery();  //执行SQL语句
                    conn.Close();  //关闭数据库连接
                    ShowMsg("管理员重置了所有账号");
                }
            }
        }
        /* 关闭窗体后触发 */
        private void FormTalk_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                string sqlUpdate = string.Format("update [User] set IsOnline={0} where Account='{1}'", 0, strAccount);  //SQL语句,更新isOnline为上线状态
                using (SqlConnection conn = new SqlConnection(connStr))  //创建数据库连接类
                {
                    using (SqlCommand cmdUpdate = new SqlCommand(sqlUpdate, conn))  //创建数据库命令类
                    {
                        conn.Open();  //打开数据库连接
                        //执行非查询命令时使用ExecuteNonQuery,会返回影响的行数
                        cmdUpdate.ExecuteNonQuery();  //执行SQL语句
                        conn.Close();  //关闭数据库连接
                        System.Environment.Exit(0);  //杀死进程
                    }
                }
            }
            catch { }  //异常捕获
        }
        /* 关闭窗体时触发 */
        private void FormTalk_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult res = MessageBox.Show("是否保存聊天记录?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);  //保存结果信息
            /// 参数1:显示文本,参数2:标题,参数3:按键类型,参数4:显示图标
            if (res == DialogResult.Yes)
            {
                if (!System.IO.Directory.Exists(_path))
                    System.IO.Directory.CreateDirectory(_path);
                /* 保存聊天记录 */
                string log_path = _path + strAccount + "_log.txt";
                using (FileStream logFileStream = new FileStream(log_path, FileMode.Create, FileAccess.Write))  //创建写入文件
                {
                    using (StreamWriter logWriter = new StreamWriter(logFileStream))
                    {
                        logWriter.Write(txtTalk.Text);
                        logWriter.Close();
                    }
                    logFileStream.Close();
                }
                e.Cancel = false;  //确认关闭窗体
            }
            else if (res == DialogResult.No)  //不保存聊天记录
            {
                e.Cancel = false;  //确认关闭窗体
            }
            else  //取消
            {
                e.Cancel = true;  //取消关闭窗体
            }
        }
        /* 点击右键菜单项时触发 */
        private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            contextMenuStrip.Items[0].Click += new EventHandler(clearTxtTalk);     //定义EvendHandler委托
        }
        /* 聊天区记录清空函数 */
        private void clearTxtTalk(object sender, EventArgs e)
        {
            this.Invoke(new Action(() =>
            {
                txtTalk.Text = "";
            }));
        }
    }
}

结尾与展示

.NET编程——利用C#实现基于Socket类的聊天室(WinForm)
WeTalk聊天室后续可以升级成可以一对一私聊的聊天室,此时需要共同约定一个消息的格式也被称为协议,在协议中隐含了特定的用来交互的信息,例如本文的聊天室所设计的消息格式为用户名:消息内容,利用:将用户名与消息内容分割开,升级成可一对一私聊的聊天室后可以把消息格式设计为接受者:发送者:消息,在服务器中把用户名与套接字用字典进行存储并查询即可实现能够一对一私聊的聊天室,读者可自行尝试升级。文章来源地址https://www.toymoban.com/news/detail-420260.html

到了这里,关于.NET编程——利用C#实现基于Socket类的聊天室(WinForm)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言Socket编程TCP简单聊天室

    这是一个使用C语言进行套接字编程实现的简单聊天室, 使用Pthread库进行多线程执行 服务端: svr.c 客户端: cli.c Makefile: Makefile 执行编译 启动服务器 启动客户端 聊天 退出

    2024年02月03日
    浏览(33)
  • 【Linux网络编程】基于UDP实现多人聊天室

    UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。 数据报格式套接字 SOCK_DGRAM 采用UDP只管发送数据而不去验证发送数据的正确性,不论传输是否被接收,数据流是否有丢失,都不再重新发

    2024年02月08日
    浏览(40)
  • Python多人聊天室-基于socket UDP协议

    使用Python编写的基于socket UDP通信的多功能即时聊天室,包含Tkinter编写的图形化聊天界面,功能包括有账号注册和登录,登录成功后可以查看在线用户,并和聊天室内的其他在线用户聊天,包含私聊和群发,能发送文字、表情包,以及文件等。 登录和注册 显示在线用户 群聊

    2024年02月11日
    浏览(40)
  • 基于Java Socket写一个多线程的聊天室(附源码)

    Socket编程是在TCP/IP上的网络编程,但是Socket在上述模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来 ​ 我们可以发现Socket就在应用程序的传输层和应用层之间,设计了一个Socket抽象层,传输层的底一层的服务提供给Socket抽象层,S

    2024年02月10日
    浏览(34)
  • 计算机网络技术与JAVA网络编程手写Socket聊天室-----JAVA入门基础教程-----计算机网络经典

    import java.io.*; import java.net.Socket; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { try { Socket socket = new Socket(\\\"127.0.0.1\\\",9090); new Thread(new Runnable() { @Override public void run() { InputStream inputStream = null; while(true) { try { inputStream = socket.getInputStream(); } catch (IOException e)

    2024年02月15日
    浏览(41)
  • Linux-Socket实现模拟群聊(多人聊天室)

    简单版本 服务端源码 客户端源码 服务器可以在特定的端口监听客户端的连接请求,若连接成功,服务器采用广播的形式向当前所有连接客户端发送该客户端登录成功消息多个客户端可以同时登录,在源码文件中可以配置最多群聊同时在线人数。服务端接收到客户端发送的群

    2024年02月10日
    浏览(77)
  • Python web实战 | 使用 Flask 实现 Web Socket 聊天室

        今天我们学习如何使用 Python 实现 Web Socket,并实现一个实时聊天室的功能。本文的技术栈包括 Python、Flask、Socket.IO 和 HTML/CSS/JavaScript。   Web Socket 是一种在单个 TCP 连接上进行全双工通信的协议。它是 HTML5 中的一部分,并且可以在浏览器和服务器之间创建实时的交互式

    2024年02月14日
    浏览(36)
  • VB.net:VB.net编程语言学习之ADO.net基本名称空间与类的简介、案例应用(实现与SQL数据库编程案例)之详细攻略

    VB.net:VB.net编程语言学习之ADO.net基本名称空间与类的简介、案例应用(实现与SQL数据库编程案例)之详细攻略 目录 ADO.net基本名称空间与类 1、ADO.net六个基础名称空间 2、ADO.net类

    2024年02月06日
    浏览(28)
  • 【你的第一个socket应用】Vue3+Node实现一个WebSocket即时通讯聊天室

    这篇文章主要是用WebSocket技术实现一个 即时通讯聊天室 ,首先先要了解为什么使用WebSocket而不是普通的HTTP协议,如果使用HTTP协议它是下面这种情况: 我发送一条消息,发送一个发送消息的请求;* 一直轮询接收别人发送的消息,不管有没有发送都要定时去调用接口。这里明

    2023年04月20日
    浏览(45)
  • 基于C# Socket实现的简单的Redis客户端

         Redis 是一款强大的高性能键值存储数据库,也是目前 NOSQL 中 最流行 比较流行的一款数据库,它在广泛的应用场景中扮演着至关重要的角色,包括但不限于缓存、消息队列、会话存储等。在本文中,我们将介绍如何基于 C# Socket 来实现一个简单的Redis客户端类 RedisClien

    2024年02月05日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包