WinForm版聊天室复习Socket通信

聊天室:服务器端-------------客户端

最终演示展示图:

一. 服务器端

  对服务端为了让主窗体后台不处理具体业务逻辑,因此对服务端进行了封装,专门用来处理某个客户端通信的过程。

而由于通信管理类中需要处理具体与某个客户端的通信业务,所以在构造函数中传入了具体的套接字对象。

  针对消息提醒:由于需要再通信管理类中进行消息提示,而需要调用主窗体的ShowMsg方法。因此将打印消息的方法通过委托传给了通信管理类的构造函数

  同理针对意外关闭的客户端连接也同样通过委托将移除客户端的方法传给了通信管理类。

  因此,再通信管理类的全局变量里就有了如下的定义:

//与某个客户端通信套接字
 Socket sokMsg = null;
//通信线程
Thread thrMsg = null;
//创建一个委托对象, 在窗体显示消息的方法
DGShowMsg dgShow = null;
//创建一个关闭连接的方法
DGCloseConn dgCloseConn = null;//通信管理类的构造函数public MsgConnection(Socket sokMsg, DGShowMsg dgShow, DGCloseConn dgCloseConn).........

  

  1.0 开始监听

    1.1.1 创建监听套接字 

      //创建监听套接字,使用ip4协议,流式传输,tcp链接      sokWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    1.1.2 绑定端口

      //获取网络节点对象
          IPAddress address = IPAddress.Parse(txbIp.Text);
          IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txbPort.Text));
          // 绑定端口(其实内部 就向系统的端口表中注册了一个端口,并指定了当前程序句柄)
          sokWatch.Bind(endPoint);

    1.1.3 设置监听队列,指限制同时处理的连接请求数,即同时处理的客户端连接请求。

      sokWatch.Listen(10);

      1.1.4  开始监听,调用监听线程 执行 监听套接字的监听方法。

      thrWatch = new Thread(WatchConncting);
          thrWatch.IsBackground = true;
          thrWatch.Start();
          ShowMsg("服务器启动!");

  2.0  服务器监听方法 + void WatchConncting() 

  void WatchConncting()
        {
            try
            {
                //循环监听客户端的连接请求。
                while (isWatch)
                {
                    //2.4开始监听,返回了一个通信套接字
                    Socket sockMsg = sokWatch.Accept();
                    //2.5 创建通信管理类
                    MsgConnection conn = new MsgConnection(sockMsg, ShowMsg, RemoveClient);

                    //将当前连接成功的【与客户端通信的套接字】的标识保存起来,并显示到列表中
                    //将远程客户端的 ip 和 端口 字符串 存入列表
                    listOnline.Items.Add(sockMsg.RemoteEndPoint.ToString());
                    //将服务器端的通信套接字存入字典集合。
                    dictConn.Add(sockMsg.RemoteEndPoint.ToString(), conn);
                    ShowMsg("有客户端连接了!");
                }
            }
            catch (Exception ex)
            {
                ShowMsg("异常" + ex);
            }
        }

  3.0 服务端向指定的客户端发送消息

    string strClient = listOnline.Text;
            if (dictConn.ContainsKey(strClient))
            {
                string strMsg = txtInput.Text.Trim();
                ShowMsg("向客户端【" + strClient + "】说:" + strMsg);
                //通过指定的套接字将字符串发送到指定的客户端
                try
                {
                    dictConn[strClient].Send(strMsg);
                }
                catch (Exception ex)
                {
                    ShowMsg("异常" + ex.Message);
                }
            }

  4.0 根据要中断的客户端ipport关闭连接 + void RemoveClient(string clientIpPort)

    //1.0 移除列表中的项
       listOnline.Items.Remove(clientIpPort);
       //2.0 关闭通信管理类
       dictConn[clientIpPort].Close();
       //3.0 从字典中移除对应的通信管理类的项
       dictConn.Remove(clientIpPort);

  5.0 选择要发送的文件

   OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                //将选中的要发送的文件路径,显示到文本框中。
                txtFilePath.Text = ofd.FileName;
            }

   6.0 发送文件

    string strClient = listOnline.Text;
            if (dictConn.ContainsKey(strClient))
            {
                string strMsg = txtInput.Text.Trim();
                //通过指定的套接字将字符串发送到指定的客户端
                try
                {
                    dictConn[strClient].SendFile(txtFilePath.Text.Trim());
                }
                catch (Exception ex)
                {
                    ShowMsg("异常" + ex.Message);
                }
            }

  7.0 向指定的客户端发送抖屏

    string strClient = listOnline.Text;
            if (dictConn.ContainsKey(strClient))
            {
                string strMsg = txtInput.Text.Trim();
                //通过指定的套接字将字符串发送到指定的客户端
                try
                {
                    dictConn[strClient].SendShake();
                }
                catch (Exception ex)
                {
                    ShowMsg("异常" + ex.Message);
                }
            }

  8.0 打印消息 + ShowMsg(string strmsg)

    this.txtShow.AppendText(strmsg + "\r\n");

  服务器端通信管理类,负责处理与某个客户端通信的过程

public class MsgConnection
    {
        //与某个客户端通信套接字
        Socket sokMsg = null;
        //通信线程
        Thread thrMsg = null;
        //创建一个委托对象, 在窗体显示消息的方法
        DGShowMsg dgShow = null;
        //创建一个关闭连接的方法
        DGCloseConn dgCloseConn = null;

        #region 1.0 构造函数
        public MsgConnection(Socket sokMsg, DGShowMsg dgShow, DGCloseConn dgCloseConn)
        {
            this.sokMsg = sokMsg;
            this.dgShow = dgShow;
            this.dgCloseConn = dgCloseConn;
            //创建通信线程,负责调用通信套接字,来接收客户端消息。
            thrMsg = new Thread(ReceiveMsg);
            thrMsg.IsBackground = true;
            thrMsg.Start(this.sokMsg);
        }
        #endregion

        bool isReceive = true;
        #region  2.0 接收客户端发送的消息
        void ReceiveMsg(object obj)
        {
            Socket sockMsg = obj as Socket;
            //3 通信套接字 监听客户端的消息,传输的是byte格式。
            //3.1 开辟了一个 1M 的空间,创建的消息缓存区,接收客户端的消息。
            byte[] arrMsg = new byte[1024 * 1024 * 1];
            try
            {
                while (isReceive)
                {
                    //注意:Receive也会阻断当前的线程。
                    //3.2 接收客户端的消息,并存入消息缓存区。
                    //并 返回 真实接收到的客户端数据的字节长度。
                    int realLength = sockMsg.Receive(arrMsg);
                    //3.3 将接收的消息转成字符串
                    string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, 0, realLength);
                    //3.4 将消息显示到文本框
                    dgShow(strMsg);
                }
            }
            catch (Exception ex)
            {
                //调用窗体类的关闭移除方法
                dgCloseConn(sokMsg.RemoteEndPoint.ToString());
                //显示消息
                dgShow("客户端断开连接!");
            }
        }
        #endregion

        #region 3.0 向客户端发送文本消息 + void Send(string msg)
        /// <summary>
        /// 3.0 向客户端发送文本消息
        /// </summary>
        /// <param name="msg"></param>
        public void Send(string msg)
        {
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(msg);
            //通过指定的套接字将字符串发送到指定的客户端
            try
            {
                sokMsg.Send(MakeNewByte("str",arrMsg));
            }
            catch (Exception ex)
            {
                dgShow("异常" + ex.Message);
            }
        }
        #endregion

        #region 4.0 向客户端发送文件 + void SendFile(string strPath)
        /// <summary>
        /// 4.0 向客户端发送文件
        /// </summary>
        /// <param name="strFilePath"></param>
        public void SendFile(string strFilePath)
        {
            //4.1 读取要发送的文件
            byte[] arrFile = System.IO.File.ReadAllBytes(strFilePath);
            //4.2 向客户端发送文件
            sokMsg.Send(MakeNewByte("file", arrFile));
        }
        #endregion

        #region 4.1 向客户端发送抖屏命令 + void SendShake()
        /// <summary>
        /// 4.1 向客户端发送抖屏命令
        /// </summary>
        public void SendShake()
        {
            sokMsg.Send(new byte[1] { 2 });
        }
        #endregion

        #region 5.0 返回带标识的新数组 + byte[] MakeNew(string type, byte[] oldArr)
        /// <summary>
        /// 返回带标识的新数组
        /// </summary>
        /// <param name="type"></param>
        /// <param name="oldArr"></param>
        /// <returns></returns>
        public byte[] MakeNewByte(string type, byte[] oldArr)
        {
            //5.1 创建一个新数组(是原数组长度 +1)
            byte[] newArrFile = new byte[oldArr.Length + 1];
            //5.2 将原数组数据复制到新数组中(从新数组下标为1的位置开始)
            oldArr.CopyTo(newArrFile, 1);
            //5.3 根据内容类型为新数组第一个元素设置标识符号
            switch (type.ToLower())
            {
                case "str":
                    newArrFile[0] = 0;  //只能存0-255之间的数值
                    break;
                case "file":
                    newArrFile[0] = 1;
                    break;
                default:
                    newArrFile[0] = 2;
                    break;
            }
            return newArrFile;
        }
        #endregion

        #region 6.0 关闭通信
        /// <summary>
        /// 关闭通信
        /// </summary>
        public void Close()
        {
            isReceive = false;
            sokMsg.Close();
            sokMsg = null;
        }
        #endregion
    }

二 .  客户端

    

   1.0 发送连接服务端请求

      try
            {
                //1.0创建连接套接字,使用ip4协议,流式传输,tcp链接
                sokMsg = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //2.0获取要链接的服务端节点
                //2.1获取网络节点对象
                IPAddress address = IPAddress.Parse(txtIp.Text);
                IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text));
                //3 向服务端发送链接请求。
                sokMsg.Connect(endPoint);
                ShowMsg("连接服务器成功!");

                //4 开启通信线程
                thrMsg = new Thread(ReceiveMsg);
                thrMsg.IsBackground = true;
                //win7, win8 需要设置客户端通信线程同步设置,才能在接收文件时打开文件选择框
                thrMsg.SetApartmentState(ApartmentState.STA);
                thrMsg.Start();
            }
            catch (Exception ex)
            {
                ShowMsg("连接服务器失败!" + ex.Message);
            }

   2.0 接收服务端消息

//准备一个消息缓冲区域
            byte[] arrMsg = new byte[1024 * 1024 * 1];
            try
            {
                while (isReceive)
                {
                    //接收 服务器发来的数据,因为包含了一个标示符,所以内容的真实长度应该-1
                    int realLength = sokMsg.Receive(arrMsg)-1;
                    switch (arrMsg[0])
                    {
                        //文本
                        case 0:
                            GetMsg(arrMsg,realLength);
                            break;
                        //文件
                        case 1:
                            GetFile(arrMsg,realLength);
                            break;
                        default:
                            ShakeWindow();
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                sokMsg.Close();
                sokMsg = null;
                ShowMsg("服务器断开连接!");
            }

  2.1  接收服务端文本消息 + GetMsg(byte[] arrContent, int realLength)  

      //获取接收的内容,去掉第一个标示符。
            string strMsg = System.Text.Encoding.UTF8.GetString(arrContent, 1, realLength);
            ShowMsg("服务器说:" + strMsg);

   2.2 保存文件 + GetFile(byte[] arrContent, int realLength)

       SaveFileDialog sfd = new SaveFileDialog();
            if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                string savaPath = sfd.FileName;
                //使用文件流,保存文件
                using (System.IO.FileStream fs = new System.IO.FileStream(savaPath, System.IO.FileMode.OpenOrCreate))
                {
                    //将收到的文件数据数组,写入硬盘。
                    fs.Write(arrContent, 1, realLength);
                }
                ShowMsg("保存文件到 【" + savaPath + "】成功!");
            }

  2.3 抖屏 + void ShakeWindow()

       // 保存当前窗体位置
            Point oldPoint = this.Location;
            for (int i = 0; i < 15; i++)
            {
                //随机生成新位置
                Point newPoint = new Point(oldPoint.X + ran.Next(8), oldPoint.Y + ran.Next(8));
                //将新位置设置给窗体
                this.Location = newPoint;
                System.Threading.Thread.Sleep(25);
                this.Location = oldPoint;
                //休息15毫秒
                System.Threading.Thread.Sleep(25);
            }

  3.0 客户端发送消息到服务端

      string strMsg = txtInput.Text.Trim();
            if (strMsg != "")
            {
                byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
                try
                {
                    sokMsg.Send(arrMsg);
                }
                catch (Exception ex)
                {
                    ShowMsg("发送消息失败!" + ex.Message);
                }
            }
            else
            {
                MessageBox.Show("未输入任何信息!");
            }

  4.0 展示消息方法 + ShowMsg(string strmsg)

this.txtShow.AppendText(strmsg + "\r\n");

注:小弟不断学习中,还希望园友指导交流。同时也作为个人学习的一个小记录。【好记性不如烂笔头】(*^__^*)

源码分享: http://download.csdn.net/detail/huayuqiang/8270749

时间: 2024-10-31 20:18:40

WinForm版聊天室复习Socket通信的相关文章

小程序版聊天室|聊天小程序|仿微信聊天界面小程序

仿微信聊天小程序weChatRoom案例,一款基于微信小程序开发的聊天室实战项目.很早之前就有开发过一个h5版聊天室,最近又在原先思路的基础上开发了个小程序版聊天室,功能效果非常接近微信聊天,实现了消息.表情发送,小程序表情解析,图片.视频上传预览,打赏.红包等微交互场景.整体界面风格及效果挺不错哒. ◆ 先睹为快 ◆ 项目中用到的弹窗插件,是自己开发的小程序弹窗组件wcPop: <!-- <>引入弹窗模板.Start --><import src="/utils/

群聊天室中socket的运用 的理解

群聊天室服务端:准备在线Socket列表:list<Socket>sockets;ServerSocket ss; while(ture){Socket s=ss.accept();sockets.add(s);new WorkThread(s).start();}WorkThread{for(int i=0;i<sockets.size();i++){sockets.get(i).getOS().write();}}

你好,博客园!!第一弹~局域网下的简易聊天室,socket与多线程简结

发觉博客园里面关于这些基本知识点的详细内容真是应有尽有,所以这里的随笔就不再重复了,就积累一下简单的用法-- 1.Socket 最近学网络编程,也就是Socket,套接字,一个用来建立链接传输数据的工具. 数据传输发生在"客户端"与"服务端"之间,下面是一种建立连接传输数据的简单方法: (1)客户端 1 try{ 2 //服务端ip 3 String ip = "127.0.0.1"; 4 //服务器端口 5 int port = 5000; 6

基于WebSocket实现网页版聊天室

WebSocket ,HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,其使用简单,应用场景也广泛,不同开发语言都用种类繁多的实现,仅Java体系中,Tomcat,Jetty,Spring等都提供了对WS的API支持.本篇不做理论探究,仅自娱自乐,简单实现网页版的聊天室功能,在实际开发场景中变通使用即可.废话不叽歪,直接撸出来—— 1  简单页面 <!DOCTYPE html> <html lang="en"> <head> &l

实现Winform与Sliverlight之间的Socket通信转换

众所周知,Winform下的Socket与Sliverlight的Socket是不可以直接通信的,原因请参考http://www.cnblogs.com/ZetaChow/archive/2009/05/16/2237347.html.以下是经本人验证过的转换方法,具体流程为:先建立一个winform client端接收已存在的winform server端发来的信息,将接收到的信息通过新建的sliverlight server端发出,再建立一个sliverlight client端接收此消息,

使用node.js实现多人聊天室(socket.io、B/S)

通过B/S架构实现多人聊天,客户端连接服务器,发送信息,服务器接收信息之后返回给客户端. 主要是通过socket.io实现浏览器和服务器之间进行实时,双向和基于事件的通信. socket.io官方文档:https://socket.io/docs/ 新建一个文件夹作为项目文件夹 新建一个js文件作为服务器,新建一个HTML文件作为客户端. 建立服务器,使用http模块的createSrever快速创建一个服务,监听端口为3000: 1 let http = require("http"

【总结】学习Socket编写的聊天室小程序

1.前言 在学习Socket之前,先来学习点网络相关的知识吧,自己学习过程中的一些总结,Socket是一门很高深的学问,本文只是Socket一些最基础的东西,大神请自觉绕路. 传输协议 TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的.可靠的.基于字节流的运输层(Transport layer)通信协议. 特点: 面向连接的协议,数据传输必须要建立连接,所以在TCP中需要连接时间. 传输数据大小限制,一旦连接建立,双方可以按统一的

java 用socket制作一个简易多人聊天室

代码: 服务器端Server import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{ public static ServerSocket server_socket; public static ArrayList<Socket> socketList=new ArrayList<Socket>(); public static void main(String [

react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面

一.前言 9月,又到开学的季节.为每个一直默默努力的自己点赞!最近都沉浸在react native原生app开发中,之前也有使用vue/react/angular等技术开发过聊天室项目,另外还使用RN技术做了个自定义模态弹窗rnPop组件. 一.项目简述 基于react+react-native+react-navigation+react-redux+react-native-swiper+rnPop等技术开发的仿微信原生App界面聊天室——RN_ChatRoom,实现了原生app启动页.As