效果

实现思路

使用TcpListener建一个服务器,接收所有客户端发送的消息,然后由服务器再发送到其他客户端

客户端使用TcpClient,发消息给服务器,接收服务器的消息,不和其他客户端直接交互

服务器端

接收客户端

开启一个线程,死循环去接收客户端.接收到之后放到一个集合里,保存起来,以便转发消息用.每个客户端都再开启一个线程,用于接收这个客户端发送的消息.

接收客户端的方法AcceptTcpClient()是阻塞方法,在程序退出释放资源时会引发异常,可以先使用Pending()方法先判断是否有挂起的链接请求,有请求的话再去接收.这样可以避免退出时引发的异常.

这里取每隔1秒接收一次.

/// <summary>
/// 接收客户端
/// </summary>
private void AcceptClient()
{
    try
    {
        while (_isAccept)
        {
            if (_listener.Pending())
            {
                TcpClient client = _listener.AcceptTcpClient();
                IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;
                _clients.Add(endpoint.ToString(), client);

                //添加到前端客户端列表
                lbx_Clients.Dispatcher.Invoke(() =>
                {
                    lbx_Clients.Items.Add(endpoint.ToString());
                });

                //接收消息线程
                Thread reciveMessageThread = new Thread(ReciveMessage);
                reciveMessageThread.Start(client);
            }
            else
            {
                Thread.Sleep();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
接收消息并转发

也是死循环接收,使用Read()方法接收.如果远程主机已关闭连接,Read()将立即返回零字节.此时跳出循环,释放资源,结束此线程.

/// <summary>
/// 接收消息
/// </summary>
/// <param name="obj">TcpClient</param>
private void ReciveMessage(object obj)
{
    TcpClient client = obj as TcpClient;
    IPEndPoint endpoint = null;
    NetworkStream stream = null;

    try
    {
        endpoint = client.Client.RemoteEndPoint as IPEndPoint;
        stream = client.GetStream();

        while (true)
        {
            ];
            //如果远程主机已关闭连接,Read将立即返回零字节
            , data.Length);
            )
            {
                #region if
                , length);

                //添加到前端消息列表
                lbx_Messages.Dispatcher.Invoke(() =>
                {
                    lbx_Messages.Items.Add(string.Format("{0}:{1}", endpoint.ToString(), msg));
                });

                //发送到其他客户端
                foreach (KeyValuePair<string, TcpClient> kvp in _clients)
                {
                    if (kvp.Value != client)
                    {
                        string writeMsg = string.Format("{0}:{1}", endpoint.ToString(), msg);
                        byte[] writeData = Encoding.UTF8.GetBytes(writeMsg);
                        NetworkStream writeStream = kvp.Value.GetStream();
                        writeStream.Write(writeData, , writeData.Length);
                    }
                }
                #endregion
            }
            else
            {
                //客户端断开连接 跳出循环
                break;
            }
        }
    }
    catch (Exception ex)
    {
        //Read是阻塞方法 客户端退出是会引发异常 释放资源 结束此线程
    }
    finally
    {
        //从前端客户端列表移除
        lbx_Clients.Dispatcher.Invoke(() =>
        {
            lbx_Clients.Items.Remove(endpoint.ToString());
        });
        //释放资源
        stream.Dispose();
        _clients.Remove(endpoint.ToString());
        client.Dispose();
    }
}

客户端

接收消息

开启线程,死循环接收服务器发送的消息.如果Read()返回0,说明服务器已关闭.

/// <summary>
/// 接收消息
/// </summary>
private void ReciveMessage()
{
    try
    {
        NetworkStream stream = _client.GetStream();
        while (true)
        {
            ];
            , data.Length);
            )
            {
                , length);

                //添加到前端消息列表
                lbx_Messages.Dispatcher.Invoke(() =>
                {
                    lbx_Messages.Items.Add(msg);
                });
            }
            else
            {
                MessageBox.Show("服务器已关闭");
                stream.Dispose();
                break;
            }
        }
    }
    catch (Exception ex)
    {
        //Read是阻塞方法 程序退出释放资源是会引发异常 不做处理 线程结束
    }
}

源码下载:

服务器端:SocketServerDemo.zip

客户端:SocketClientDemo.zip

Socket入门笔记 用TcpClient实现一个简易聊天室的更多相关文章

  1. Python开发【笔记】:aiohttp搭建简易聊天室

    简易聊天室: 1.入口main.py import logging import jinja2 import aiohttp_jinja2 from aiohttp import web from a ...

  2. vue + socket.io实现一个简易聊天室

    vue + vuex + elementUi + socket.io实现一个简易的在线聊天室,提高自己在对vue系列在项目中应用的深度.因为学会一个库或者框架容易,但要结合项目使用一个库或框架就不是那 ...

  3. 真刀实战地搭建React+Webpack+Express搭建一个简易聊天室

    一.前面bb两句 因为自惭(自残)webpack配置还不够熟悉,想折腾着做一个小实例熟悉.想着七夕快到了,做一个聊天室自己和自己聊天吧哈哈.好了,可以停止bb了,说一下干货. 二. 这个项目能学到啥? ...

  4. 原生NodeJs制作一个简易聊天室

    准备工作 安装NodeJs环境 安装编译器Sublime 如果网速不理想,可以百度一下如何加快npm的速度~ 使用node搭建一个简单的网站后台 做完准备工作之后,新建文件夹chatroom,在cha ...

  5. Socket实现简易聊天室,Client,Server

    package seday08; import java.io.BufferedWriter;import java.io.OutputStream;import java.io.OutputStre ...

  6. node+websocket创建简易聊天室

    关于websocket的介绍太多,在这就不一一介绍了,本文主要实现通过websocket创建一个简易聊天室,就是90年代那种聊天室 服务端 1.安装ws模块,uuid模块,ws是websocket模块 ...

  7. node.js+websocket实现简易聊天室

    (文章是从我的个人主页上粘贴过来的,大家也可以访问我的主页 www.iwangzheng.com) websocket提供了一种全双工客户端服务器的异步通信方法,这种通信方法使用ws或者wss协议,可 ...

  8. php_D3_“简易聊天室 ”实现的关键技术 详解

                      PHP+MySQL实现Internet上一个简易聊天室的关键技术  系统目标: 聊天室使用数据库汇集每个人的发言,并可将数据库内的发言信息显示在页面,让每个用户都可 ...

  9. 示例:Socket应用之简易聊天室

    在实际应用中,Server总是在指定的端口上监听是否有Client请求,一旦监听到Client请求,Server就会启动一个线程来响应该请求,而Server本身在启动完线程之后马上又进入监听状态. 示 ...

随机推荐

  1. 理解ES6中的Promise

    一.Promise的作用 在ajax请求数据的过程中,我们可以异步拿到我们想要的数据,然后在回调中做相应的数据处理. 这样做看上去并没有什么麻烦,但是如果这个时候,我们还需要做另外一个ajax请求,这 ...

  2. sql 语句查询 sqlserver 数据库 MAC 地址

    declare @dbid int set @dbid=db_id('dbName') select distinct hostname, db_name(dbid), net_address, lo ...

  3. ghostscript远程代码执行漏洞复现

    这两天网上ghostscript又又有漏洞信息了,但是没有poc,于是找找资料把今年8月21日的那个验证下 1.关于ghostscript Ghostscript是一套建基于Adobe.PostScr ...

  4. python基础 - 变量与运算符

    变量与运算符 变量 定义一个变量 a = [1,2,3,4,5,6] print(a) # [1,2,3,4,5,6] 变量命名要求: 首字母不能是数字 只能包含字符数字下划线 不能是关键字 type ...

  5. 简易 Token 验证的实现

    简易 Token 验证的实现 前言 在我们的服务器和客户端的交互中,由于我们的业务中使用 RESTful API 的形式和客户端交互,而 API 又是无状态的,无法帮助我们识别这一次和上一次的请求由谁 ...

  6. Axios发送请求时params和data的区别

    在使用axios时,注意到配置选项中包含params和data两者,以为他们是相同的,实则不然. 因为params是添加到url的请求字符串中的,用于get请求. 而data是添加到请求体(body) ...

  7. arcgis api for javascript中使用proxy.jsp

    当我们使用arcgis api for javascript 查询arcgis服务时,如果查询的参数很长时,通过get方式提交会查询不到结果,因为get方式提交的参数有长度限制,需要通过代理的方式使用 ...

  8. Djando 的 cmd命令

    1.黑窗口命令 安装Django ​ pip install django==1.11.16 -i https://pypi.doubanio.com/simple/ 创建项目 ​ django-ad ...

  9. 合并多个Excel文件

    这条分享来自百度经验https://jingyan.baidu.com/article/e6c8503cb6ed7ee54e1a1811.html

  10. 让你提前认识软件开发(31):数据库脚本中的begin与end

    版权声明:本文为博主原创文章.对文章内容有不论什么意见或建议,欢迎与作者单独交流.作者QQ(微信):245924426. https://blog.csdn.net/zhouzxi/article/d ...