基于socket、多线程的客户端服务器端聊天程序

简介:

服务器端:



using System; using System.Windows.Forms; using System.Net.Sockets; using System.Net;//IPAddress,IPEndPoint(ip和端口)类 using System.Threading; using System.Collections.Generic; using System.IO; namespace MyChatRoomServer {     public partial class Server : Form     {         public Server()         {             InitializeComponent();             //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查             TextBox.CheckForIllegalCrossThreadCalls = false;         }          Thread threadWatch = null;//负责监听客户端连接请求的线程         Socket socketWatch = null;//负责监听的套接字          private void btnBeginListen_Click(object sender, EventArgs e)         {             //创建服务端负责监听的套接字,参数(IPV4寻址协议,使用流格式,使用Tcp传输协议)             socketWatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);             //获得文本框中IP地址对象             IPAddress address = IPAddress.Parse(txtIP.Text.Trim());             //获取端口号             IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));             //将负责监听的套接字绑定到唯一的IP和端口上             socketWatch.Bind(endpoint);             //设置监听队列的长度,意思就是同时发送请求只能10个             socketWatch.Listen(10);             //创建一个新的套接字专门负责跟客户端通信,注意:Accept方法会阻塞当前程序             //Socket sockConnection = socketWatch.Accept();              //创建负责监听的线程,并传入监听的方法             threadWatch = new Thread(WatchConnecting);             threadWatch.IsBackground = true;//设置为后台线程,只要前台的一结束,那么程序就结束             threadWatch.Start();//启动线程              ShowMsg("服务器启动监听成功!");         }         //保存了服务器端所有负责和客户端通信的套接字         Dictionary<string, Socket> dict = new Dictionary<string, Socket>();         //保存了服务器端所有负责调用通信套接字Recive方法的线程         Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();         //Socket sokConnection = null;//负责通信的套接字         /// <summary>         /// 监听客户端请求的方法         /// </summary>         void WatchConnecting()         {             while (true) //加上循环持续不断的监听新的客户端的连接             {                 //开始监听客户端连接请求,注意:Accept方法会阻断当时线程                 Socket sokConnection = socketWatch.Accept();                 //将列表控件中追加一个客户端的ip端口字符串,作为客户端的唯一标识                 lblOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());                 //将与客户端通信的套接字对象sokConnection添加到键值对集合中,并以客户端IP端口作为键                 dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);                 //创建一个委托                 ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);                 //创建通信线程                 Thread thr = new Thread(pts);                 thr.IsBackground = true; //设置为后台                 thr.Start(sokConnection); //将线程的参数传入                 dictThread.Add(sokConnection.RemoteEndPoint.ToString(),thr);                 ShowMsg("客户端连接成功!"+sokConnection.RemoteEndPoint.ToString());             }         }         /// <summary>         /// 服务端负责监听客户端发来的数据方法         /// </summary>         void RecMsg(object socketClientPara)         {             Socket socketClient = socketClientPara as Socket;             while (true)             {                 //定义一个2M的接受数据的缓存区                 byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间                 //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度                 int length = socketClient.Receive(arrMsgRec);                 try                 {                     length = socketClient.Receive(arrMsgRec);                 }                 catch (SocketException ex)                 {                     ShowMsg("异常" + ex.Message+",RemoteEnd=" + socketClient.RemoteEndPoint.ToString());                     //从通信套接字集合中删除被中断连接的通信套接字                     dict.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的端口和IP                     //从通信线程中删除被中断的连接通信线程对象                     dictThread.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的线程                     //有异常则跳出,不执行后面的                     //从列表中删除被中断的连接IP:port                     lblOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());                     break;                     //return;                 }                 catch (Exception ex)                 {                     ShowMsg("异常" + ex.Message);                     //有异常则跳出,不执行后面的                     return;                 }                 if (arrMsgRec[0] == 0)//判断传过来的第一个数据是0,则代表是文本数据                 {                     //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符                     string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);                     ShowMsg(strMsgRec);                 }                 //如果是1,则代表发送过来的是文件数据(文件/图片...)                 else if (arrMsgRec[0] == 1)                 {                     //保存文件选择框对象                     SaveFileDialog sfd = new SaveFileDialog();                     if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)                     {                         string fileSavePath = sfd.FileName;//获得文件保存路径                         //创建文件流,让文件流来根据路径创建一个文件                         using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))                         {                             fs.Write(arrMsgRec,1,length-1);                             ShowMsg("文件保存成功:"+fileSavePath);                         }                     }                 }             }         }          void ShowMsg(string msg)         {             txtMsg.AppendText(msg + "\r\n");         }          private void btnCloseServer_Click(object sender, EventArgs e)         {              //threadWatch.Abort();//关闭线程              //ShowMsg("服务器启动监听成功!");         }         //发送消息到客户端         private void btnSend_Click(object sender, EventArgs e)         {             if (string.IsNullOrEmpty(lblOnline.Text))             {                 MessageBox.Show("请在左侧选择要发送的好友");             }             else             {                 string strMsg = txtMsgSend.Text.Trim();                 //将要发送的字符串转成utf8对应的字节数组                 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);                 string strClientKey = lblOnline.Text;                 //通过key找到字典集合中对应的与某个用户客户端通信的套接字的send方法,发数据给对方                 try                 {                     dict[strClientKey].Send(arrMsg);                     //一旦上面的出现异常,下面的就不执行                     ShowMsg("发送了数据出去:" + strMsg);                 }                 //sokConnection.Send(arrMsg);                 catch (SocketException ex)                 {                     ShowMsg("发送时异常:"+ex.Message);                     return;                 }                 catch (Exception ex)                 {                     ShowMsg("发送时异常:" + ex.Message);                     return;                 }                              }         }         //服务器群发消息         private void btnSendToAll_Click(object sender, EventArgs e)         {             string strMsg = txtMsgSend.Text.Trim();             //将要发送的字符串转成utf8对应的字节数组             byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);             //便利当前字典里面所有的通信套接字             foreach (Socket s in dict.Values)             {                 s.Send(arrMsg);             }             ShowMsg("群发完毕!:)");         }     } } 


客户端:



using System; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO;   namespace MyChatRoomClient {     public partial class FChatClient : Form     {         public FChatClient()         {             InitializeComponent();             //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查             //关闭跨线程检查             TextBox.CheckForIllegalCrossThreadCalls = false;         }           IPAddress address = null;         IPEndPoint endpoint = null;         Socket socketClient = null; //客户端套接字             Thread threadClient = null;//客户端负责接受服务端发来的消息的线程         //客户端发送请求到服务器         private void btnConnect_Click(object sender, EventArgs e)         {             address = IPAddress.Parse(txtIP.Text.Trim());             endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));             socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);             try             {                 socketClient.Connect(endpoint);                 //创建线程,监听服务器端发来的消息                 threadClient = new Thread(RecMsg);                 threadClient.IsBackground = true;                 threadClient.Start();                              }             catch (Exception ee)             {                 MessageBox.Show(ee.ToString());             }         }         /// <summary>         /// 监听服务器端发来的消息         /// </summary>         void RecMsg()         {             while (true)             {                 //定义一个2M的接受数据的缓存区                 byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间                 //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度                 int length = socketClient.Receive(arrMsgRec);                 //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符                 string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);                 ShowMsg(strMsgRec);             }         }         #region 在窗体文本框中显示消息-void ShowMsg(string msg)         /// <summary>         /// 在窗体文本框中显示消息         /// </summary>         /// <param name="msg">消息</param>         void ShowMsg(string msg)         {             txtMsg.AppendText(msg + "\r\n");         }          #endregion           #region 选择要发送的文件-btnChooseFile_Click         //选择要发送的文件         private void btnChooseFile_Click(object sender, EventArgs e)         {             OpenFileDialog ofd = new OpenFileDialog();             if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)             {                 txtFilePath.Text = ofd.FileName;             }         }         #endregion               //向服务器发送文本消息         private void btnSendMsg_Click(object sender, EventArgs e)         {             string strMsg = txtMsgSend.Text.Trim();             byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);             byte[] arrMsgSend = new byte[arrMsg.Length + 1];             arrMsgSend[0] = 0;//设置标识位,0代表是文字             Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);             socketClient.Send(arrMsgSend);             ShowMsg("我发送了:" + strMsg);           //向服务端发送文件         private void btnSendFile_Click(object sender, EventArgs e)         {             //用文件流打开用户选择的文件             using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))             {                 byte[] arrFile = new byte[1024*1024*2];//定义一个2M缓存区                 //将文件数据读到数组arrFile中,并获得读取的真是数据长度                 int length = fs.Read(arrFile,0,arrFile.Length);                 byte[] arrFileSend = new byte[length + 1];                 arrFileSend[0] = 1;//代表发送的是文件数据                 //for (int i = 0; i < length; i++)                 //{                 //    arrFileSend[i + 1] = arrFile[i];                 //}                 //数据块的拷贝,将arrFile从第0个开始拷贝,拷贝到arrFileSend,从第一个开始存放                 Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);                 //arrFile.CopyTo(arrFileSend,length);只能从0开始拷贝                 //发送了包含了标识位的新数据到服务端                 socketClient.Send(arrFileSend);             }         }              } }

源码文件:http://download.csdn.net/detail/s10141303/5892933



















本文转蓬莱仙羽51CTO博客,原文链接:http://blog.51cto.com/dingxiaowei/1366443,如需转载请自行联系原作者

相关文章
|
23天前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
97 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
23天前
|
JSON 数据格式 Python
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
本文介绍了如何使用Python的socket模块实现客户端到服务器端的文件传输,包括客户端发送文件信息和内容,服务器端接收并保存文件的完整过程。
93 1
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
102 0
|
4月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
78 0
|
18天前
|
弹性计算 网络安全
阿里云国际OpenAPI多接口快速管理ECS服务器教程
阿里云国际OpenAPI多接口快速管理ECS服务器教程
|
1天前
|
弹性计算
阿里云2核16G服务器多少钱一年?亲测价格查询1个月和1小时收费标准
阿里云2核16G服务器提供多种ECS实例规格,内存型r8i实例1年6折优惠价为1901元,按月收费334.19元,按小时收费0.696221元。更多规格及详细报价请访问阿里云ECS页面。
22 9
|
1天前
|
弹性计算 异构计算
2024年阿里云GPU服务器多少钱1小时?亲测价格查询方法
2024年阿里云GPU服务器每小时收费因实例规格不同而异。可通过阿里云GPU服务器页面选择“按量付费”查看具体价格。例如,NVIDIA A100的gn7e实例为34.742元/小时,NVIDIA A10的gn7i实例为12.710156元/小时。更多详情请访问阿里云官网。
22 2
|
7天前
|
存储 弹性计算 NoSQL
"从入门到实践,全方位解析云服务器ECS的秘密——手把手教你轻松驾驭阿里云的强大计算力!"
【10月更文挑战第23天】云服务器ECS(Elastic Compute Service)是阿里云提供的基础云计算服务,允许用户在云端租用和管理虚拟服务器。ECS具有弹性伸缩、按需付费、简单易用等特点,适用于网站托管、数据库部署、大数据分析等多种场景。本文介绍ECS的基本概念、使用场景及快速上手指南。
35 3
|
12天前
|
存储 弹性计算 编解码
通过阿里云的活动租赁云服务器时如何选择实例规格?选择指南参考
新手用户通过阿里云的活动租赁云服务器的时候实例规格应该怎么选?目前在阿里云的活动中,可选的云服务器类型除了轻量应用服务器之外,云服务器的主要实例规格有经济型e、通用算力型u1和计算型c7与c8y、通用型g7与g8y、内存型r7与r8y等实例,但是对于新手来说,由于是初次购买,实例规格往往不知道怎么选择了。本文为大家展示阿里云目前活动中各云服务器实例规格性能、适用场景以及选择指南参考。
|
16天前
|
弹性计算 开发框架 .NET
阿里云服务器购买教程及云服务器地域、实例、操作系统、带宽等参数选择指南
对于初次购买阿里云服务器的用户来说,想使用阿里云服务器搭建网站或者运行APP、小程序等项目,第一步就是要先购买阿里云服务器,下面小编以图文形式给大家介绍一下阿里云服务器的购买流程,以及购买过程中如何云服务器地域、实例、带宽等关键配置和选择这些参数的一些注意事项,以供参考。