# winform实现一个服务端和多个客户端进行通信

2023-11-17

        参看此链接        http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html

        在上述代码的基础上进行了修改,包括一些捕获异常以及按钮的应用,扩充了一个listbox确保服务端可以选择和不同的客户端进行通信。

服务器代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Collections;
namespace chat1
{
        
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            StartPosition = FormStartPosition.CenterScreen;

            //关闭对文本框的非线程操作检查
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }
        string RemoteEndPoint;     //客户端的网络结点
        Thread threadwatch = null;//负责监听客户端的线程
        Socket socketwatch = null;//负责监听客户端的套接字

        //创建一个和客户端通信的套接字
        Dictionary<string, Socket> dic = new Dictionary<string, Socket> { };   //定义一个集合,存储客户端信息

        
        private void button1_Click(object sender, EventArgs e)
        {
            this.button1.Enabled = false;
            //定义一个套接字用于监听客户端发来的消息,包含三个参数(IP4寻址协议,流式连接,Tcp协议)
            socketwatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

            //服务端发送信息需要一个IP地址和端口号
            IPAddress address = IPAddress.Parse(textBox1.Text.Trim());//获取文本框输入的IP地址
            //将IP地址和端口号绑定到网络节点point上
            IPEndPoint point = new IPEndPoint(address,int.Parse(textBox2.Text.Trim()));//获取文本框上输入的端口号
            //此端口专门用来监听的
            //监听绑定的网络节点
            socketwatch.Bind(point);
            //将套接字的监听队列长度限制为20
            socketwatch.Listen(20);            
            //创建一个监听线程
            threadwatch = new Thread(watchconnecting);             
            //将窗体线程设置为与后台同步,随着主线程结束而结束
            threadwatch.IsBackground = true;   
           //启动线程   
            threadwatch.Start();   
           //启动线程后 textBox3文本框显示相应提示
           textBox3.AppendText("开始监听客户端传来的信息!" + "\r\n");          
        }


        void OnlineList_Disp(string Info)
        {
            listBoxOnlineList.Items.Add(Info);   //在线列表中显示连接的客户端套接字
        }

       
        //监听客户端发来的请求
        private void watchconnecting()
        {
            Socket connection = null;
            while (true)  //持续不断监听客户端发来的请求   
            {
                try  
                {   
                    connection = socketwatch.Accept();  
                }   
                catch (Exception ex)   
                {  
                    textBox3.AppendText(ex.Message); //提示套接字监听异常   
                    break;   
                 }
                //获取客户端的IP和端口号
                IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address;
                int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port;

                //让客户显示"连接成功的"的信息
                string sendmsg = "连接服务端成功!\r\n" + "本地IP:" + clientIP + ",本地端口" + clientPort.ToString();
                byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg); 
                connection.Send(arrSendMsg);

                RemoteEndPoint = connection.RemoteEndPoint.ToString(); //客户端网络结点号
                textBox3.AppendText("成功与" + RemoteEndPoint + "客户端建立连接!\t\n");     //显示与客户端连接情况
                dic.Add(RemoteEndPoint, connection);    //添加客户端信息
                OnlineList_Disp(RemoteEndPoint);    //显示在线客户端              
                //IPEndPoint netpoint = new IPEndPoint(clientIP,clientPort);                
                IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint;                
                //创建一个通信线程    
                ParameterizedThreadStart pts = new ParameterizedThreadStart(recv);
                Thread thread = new Thread(pts);   
                thread.IsBackground = true;//设置为后台线程,随着主线程退出而退出   
                //启动线程   
                thread.Start(connection);   
            }   
        }

  
        ///   
        /// 接收客户端发来的信息    
        ///   
        ///客户端套接字对象  
        private void recv(object socketclientpara)
        {            
            Socket socketServer = socketclientpara as Socket;
            while (true)   
            {   
                //创建一个内存缓冲区 其大小为1024*1024字节  即1M   
                byte[] arrServerRecMsg = new byte[1024 * 1024];   
                //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度  
                try
                {
                    int length = socketServer.Receive(arrServerRecMsg);

                    //将机器接受到的字节数组转换为人可以读懂的字符串   
                    string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);                    
                    //将发送的字符串信息附加到文本框txtMsg上   
                    textBox3.AppendText("客户端:" + socketServer.RemoteEndPoint+",time:" + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n\n");
                }
                catch (Exception ex)
                {
                    textBox3.AppendText("客户端"+socketServer.RemoteEndPoint+"已经中断连接"+"\r\n"); //提示套接字监听异常 
                    listBoxOnlineList.Items.Remove(socketServer.RemoteEndPoint.ToString());//从listbox中移除断开连接的客户端
                    socketServer.Close();//关闭之前accept出来的和客户端进行通信的套接字
                    break;   
                }
            }          
        }


        ///    
        /// 获取当前系统时间的方法   
        ///    
        /// 当前时间   
        private DateTime GetCurrentTime()   
        {   
            DateTime currentTime = new DateTime();   
            currentTime = DateTime.Now;   
            return currentTime;   
        }


        //发送信息到客户端 
        private void button2_Click(object sender, EventArgs e)
        {
            string sendMsg = textBox4.Text.Trim();  //要发送的信息
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg);   //将要发送的信息转化为字节数组,因为Socket发送数据时是以字节的形式发送的

            if (listBoxOnlineList.SelectedIndex == -1)
            {
                MessageBox.Show("请选择要发送的客户端!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
            else
            {
                string selectClient = listBoxOnlineList.Text;  //选择要发送的客户端
                dic[selectClient].Send(bytes);   //发送数据
                textBox4.Clear();
                textBox3.AppendText(label4.Text + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");   
            }                   
        }

          
        //快捷键 Enter 发送信息 
       private void textBox4_KeyDown(object sender, KeyEventArgs e)
        {
            //如果用户按下了Enter键   
            if (e.KeyCode == Keys.Enter)
            {
                string sendMsg = textBox4.Text.Trim();  //要发送的信息
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); 
                if (listBoxOnlineList.SelectedIndex == -1)
                {
                    MessageBox.Show("请选择要发送的客户端!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }
                else
                {

                    string selectClient = listBoxOnlineList.Text;  //选择要发送的客户端
                    dic[selectClient].Send(bytes);   //发送数据
                    textBox4.Clear();
                    textBox3.AppendText(label4.Text + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");

                }
            }
        }


        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult result = MessageBox.Show("是否退出?选否,最小化到托盘", "操作提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
            {
                this.Dispose();
            }
            else if (result == DialogResult.Cancel)
            {
                e.Cancel = true;
            }
            else
            {
                e.Cancel = true;
                this.WindowState = FormWindowState.Minimized;
                this.Visible = false;
                this.notifyIcon1.Visible = true;
                this.ShowInTaskbar = false;
            }
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            base.Visible = true;
            this.notifyIcon1.Visible = false;
            this.ShowInTaskbar = true;
            //base.Show();
            base.WindowState = FormWindowState.Normal;
        } 
    }
}

 客户端的代码:

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


namespace chat2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            StartPosition = FormStartPosition.CenterScreen;
            //关闭对文本框的非法线程操作检查
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        //创建 1个客户端套接字 和1个负责监听服务端请求的线程
        Thread threadclient = null;
        Socket socketclient = null;
        List<IPEndPoint> mlist =new List<IPEndPoint>();
        private void button1_Click(object sender, EventArgs e)
        {
            //SocketException exception;
            this.button1.Enabled = false;
            //定义一个套接字监听
            socketclient = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

            //获取文本框中的IP地址
            IPAddress address = IPAddress.Parse(textBox1.Text.Trim());

            //将获取的IP地址和端口号绑定在网络节点上
            IPEndPoint point = new IPEndPoint(address,int.Parse(textBox2.Text.Trim()));

            try
            {
                //客户端套接字连接到网络节点上,用的是Connect
                socketclient.Connect(point);
            }
            catch (Exception )
            {
                //MessageBox.
                MessageBox.Show("连接失败\r\n");
                this.button1.Enabled = true;
                return;
            }
            
            threadclient=new Thread(recv);

            threadclient.IsBackground=true;

            threadclient.Start();
        }

        
        
        // 接收服务端发来信息的方法  
        private void recv()//
        {
            int x = 0;
            while (true)//持续监听服务端发来的消息
            {
                try
                {
                    //定义一个1M的内存缓冲区,用于临时性存储接收到的消息
                    byte[] arrRecvmsg = new byte[1024 * 1024];

                    //将客户端套接字接收到的数据存入内存缓冲区,并获取长度
                    int length = socketclient.Receive(arrRecvmsg);

                    //将套接字获取到的字符数组转换为人可以看懂的字符串
                    string strRevMsg = Encoding.UTF8.GetString(arrRecvmsg, 0, length);
                    if (x == 1)
                    {
                        textBox3.AppendText("服务器:" + GetCurrentTime() + "\r\n" + strRevMsg + "\r\n\n");

                    }
                    else
                    {

                        textBox3.AppendText(strRevMsg + "\r\n\n");
                        x = 1;
                    }
                }
                catch(Exception ex)
                {
                    textBox3.AppendText("远程服务器已经中断连接"+"\r\n");
                    this.button1.Enabled = true;
                    break;
                }
            }
        }

        //获取当前系统时间
        private DateTime GetCurrentTime()
        {
            DateTime currentTime = new DateTime();
            currentTime = DateTime.Now;
            return currentTime; 
        }

        //发送字符信息到服务端的方法
        private void ClientSendMsg(string sendMsg)
        {
            //将输入的内容字符串转换为机器可以识别的字节数组   
            byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);   
            //调用客户端套接字发送字节数组   
            socketclient.Send(arrClientSendMsg);   
            //将发送的信息追加到聊天内容文本框中   
            textBox3.AppendText(this.label4.Text+": " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n\n"); 
        }



        //点击按钮button2 向服务端发送信息 
        private void button2_Click(object sender, EventArgs e)
        {
            //调用ClientSendMsg方法 将文本框中输入的信息发送给服务端   
            ClientSendMsg(textBox4.Text.Trim());
            textBox4.Clear();
        }

        
       
        private void textBox4_KeyDown_1(object sender, KeyEventArgs e)
        {
            //当光标位于文本框时 如果用户按下了键盘上的Enter键
            if (e.KeyCode == Keys.Enter)
            {
                //则调用客户端向服务端发送信息的方法  
                ClientSendMsg(textBox4.Text.Trim());
                textBox4.Clear();

            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult result = MessageBox.Show("是否退出?选否,最小化到托盘", "操作提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
            {
                this.Dispose();
            }
            else if (result == DialogResult.Cancel)
            {
                e.Cancel = true;
                
            }
            else
            {
                e.Cancel = true;
                this.WindowState = FormWindowState.Minimized;
                this.Visible = false;
                this.notifyIcon1.Visible = true;
                this.ShowInTaskbar = false;
            }
        }



        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            base.Visible = true;
            this.notifyIcon1.Visible = false;
            this.ShowInTaskbar = true;
            //base.Show();
            base.WindowState = FormWindowState.Normal;
        }
    }
}

 

 

 

 

程序还有不足,后期还会继续改进

客户端在断开连接后,应该把socket关闭;

if (result == DialogResult.Yes)

 {
           if (socketclient == null)
            {
                    this.Dispose();
            }
            else
            {
                socketclient.Close();
                this.Dispose();
             }

                      }

其他错误,欢迎大家指出,需要完善的地方,也希望大家给出建议

作者:Chuck Lu GitHub

https://www.cnblogs.com/chucklu/archive/2012/11/23/2784733.html

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

# winform实现一个服务端和多个客户端进行通信 的相关文章

  • 编译Linux内核

    一 首先下载好内核源代码 首先在命令行窗口中执行下面这条命令 apt cache search linux source 执行用这条命令系统会提示你安装适合你内核版本的内核源码 然后选择其中一种内核版本 到www kernel org内核官
  • Jenkins持续集成项目实践 —— 基于Python Selenium自动化测试(二)

    上一篇讲了如何搭建jenkins 这篇主要讲 怎么将自动化代码与jenkins衔接起来 jenkins上运行的两种方式 第一种 在jenkins上面运行本地代码 操作如下 新建项目 项目名称根据自己项目情况填写并选择自由模式 进行配置根据如
  • R语言入门教程知识 第二章 赋值代码与命名规则

    快捷键 新建脚本 Ctrl shift N 清理控制台快捷键 Ctrl L 赋值快捷键ALT 赋值法 1 符号方式赋值 对象名 lt 对象值 2 函数方式赋值 assign 对象 对象值 三大基础函数 numeric 数值型向量 logic
  • 上网原理大透析(MAC,IP,掩码,DNS)

    一 概述 1 1 五层模型 互联网的实现 分成好几层 每一层都有自己的功能 就像建筑物一样 每一层都靠下一层支持 用户接触到的 只是最上面的一层 根本没有感觉到下面的层 要理解互联网 必须从最下层开始 自下而上理解每一层的功能 如何分层有不
  • 内联函数的使用及注意点

    内联函数并不总是内联 Inline function是在C 中引入的一种机制 它可以拓展函数代码 避免调用函数的额外开销 在Linux环境下 gcc编译选项必须加上优化选项才能使inline有效 inline与static的关系 在这儿有一
  • Kafka之基础笔记

    1 kafka offset 存储 1 1 去zookeeper依赖 比较广为人知的Kafka offset存储方式为zookeeper 在0 8版本时 默认依然是zk 但是此时其实已经出现另外一种offset存储方式了 Kafka以 co

随机推荐

  • 如何在ios中集成微信登录功能

    在ios中集成微信的登录功能有两种方法 1 用微信原生的api来做 这样做的好处就是轻量级 程序负重小 在Build Settings 中这样设置 然后设置 友盟的设置同上 但是要注意 加入你需要的所有框架到应用程序中
  • VUE3父子组件传值defineProps() 和 defineEmits()

    defineProps 和 defineEmits 都是只能在
  • 二十三种设计模式第十九篇--命令模式

    命令模式是一种行为设计模式 它将请求封装成一个独立的对象 从而允许您以参数化的方式将客户端代码与具体实现解耦 在命令模式中 命令对象充当调用者和接收者之间的中介 这使您能够根据需要将请求排队 记录请求日志 撤销操作等 命令模式的核心组成部分
  • yaml数组解析_【社区精选】一文看懂 YAML

    本文原作者为陈皮皮 2020年6月11日发布于 微信小游戏开放社区 原文 一文看懂 YAML 前言 YAML 并不是一种新奇的语言 YAML 首次发表于 2001 年 距离现在已经过去差不多 20 个年头 YAML 虽然不如 JSON XM
  • 16/32/64平台上不同内置数据类型所占的字节数大小

    https blog csdn net qq 32535249 article details 88077019 utm medium distribute pc relevant none task blog baidujs title
  • 银行对公账户编码规则

    银行对公账户编码规则 1 工商银行 2 农业银行 3 中国银行 4 建设银行 5 邮储银行 更多 1 工商银行 中国工商银行对公账户共19位 其中1 4位为地区代码 详见代码表 5 8位为网点代码 9 10位为应用号 11 17位为序号 1
  • 计算机网络复习7---差错检验CRC循环冗余检验

    这部分在书中P74页 具体步骤已经写的非常清楚了 这里不做详细介绍 这里着重说一点 是我在刚才复习时才发现一直弄错的地方 希望大家不要出错 在模2除法时 被除数应该是 待传数据 n个0 被除数不是 代传数据 这个很重要 n个0怎么确定呢 书
  • easyexcel 学习

    EasyExcel EasyExcel是一个基于Java的简单 省内存的读写Excel的开源项目 在尽可能节约内存的情况下支持读写百M的Excel 语雀 写excel 引言 首先简单讲解一下EasyExcel的写语法 方便理解后续的代码 最
  • 什么是深度学习,如何进行深度学习?

    深度学习的必要性 改革开放40年 基础教育研究与实践的最大成就之一 就是树立了 学生是教育主体 的观念 但是 在课堂教学中 学生并未真正成为主体 大多数课堂教学也没有发生根本变化 为什么 因为大多数教学改革尚未抓住教学的根本 对课堂教学的研
  • java开发sublime插件_开发者最常用的8款Sublime text 3插件

    5 SublimeCodeIntel Sublime Code Intel 作为一个代码提示和补全插件 支持Javascript Mason XBL XUL RHTML SCSS python HTML Ruby Python3 XML S
  • 【常见的编解码场景】

    常见的编解码场景 一 编码与解码 1 1 字符编解码 字符集和编码 编解码过程分析 编解码举例 1 2 http url编码 1 3 http body编码 1 4 压缩编码 1 5 对象编码 netty框架中的编码解码 dubbo框架的编
  • 微信小程序中主包和分包过大,详解分包问题

    哈喽 大家好呀 小韵携原创博文给大家请安啦 前言 开发微信小程序时 若项目比较大 必定要分包 当项目过于大时 则需要细致 谨慎地对项目进行分包优化和精简 这是一个不可避免地问题 网上的大多数分包优化都是针对于小项目的普通官方分包优化 并未将
  • 利用Java EE相关技术实现一个简单的购物车系统

    利用JSP编程技术实现一个简单的购物车程序 具体要求如下 1 用JSP编程实现一个登录页面 登录信息中有用户名和密码 分别用两个按钮来提交和重置登录信息 另外 登录页面请实现记住密码功能 2 编写一个JSP程序来获取用户提交的登录信息并查询
  • Redis Cluster集群主从切换踩坑记

    因为项目的原因采用了Redis Cluster 3主3从 每台主机1主1从 集群信息如下 10 135 255 72 20011 gt cluster nodes 7b662b36489a6240aa21d1cf7b04b84019254b
  • STL源码剖析之一:空间适配器(allocator)

    空间适配器是 所有组件的核心 每个操作系统都有自己的内存分配器 他承担着内存分配 管理 释放 作为模版参数传递到每个容器去 allocate函数分配一片连续的未被构造的空间备用 deallocate 函数释放空间 construct函数调用
  • nodejs 中 token 的使用

    前言 token 验证 在设计登录注册和一些权限接口时发挥作用 以nodejs为例 谈一谈jsonwebtoken的使用 正文 一 安装 npm i jsonwebtoken 二 使用 首先 需要提供一个密匙 也就是一个字符串 用于toke
  • FPGA零基础学习之Vivado-ROM使用教程

    FPGA零基础学习之Vivado ROM使用教程 本系列将带来FPGA的系统性学习 从最基本的数字电路基础开始 最详细操作步骤 最直白的言语描述 手把手的 傻瓜式 讲解 让电子 信息 通信类专业学生 初入职场小白及打算进阶提升的职业开发者都
  • 云原生之使用Docker部署Magma导航页

    云原生之使用Docker部署Magma导航页 一 Magma导航页介绍 1 1 Magma导航页简介 1 2Magma导航页特点 二 本地环境介绍 2 1 本地环境规划 2 2 本次实践介绍 三 本地环境检查 3 1 检查Docker服务状
  • 扛住阿里双十一高并发流量,Sentinel是怎么做到的?

    Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景 本文介绍阿里开源限流熔断方案 Sentinel 功能 原理 架构 快速入门以及相关框架比较 基本介绍 1 名词解释 服务限流 当系统资源不够 不足以应对大量请求 对系统
  • # winform实现一个服务端和多个客户端进行通信

    参看此链接 http www cnblogs com longwu archive 2011 08 25 2153636 html 在上述代码的基础上进行了修改 包括一些捕获异常以及按钮的应用 扩充了一个listbox确保服务端可以选择和不