Socket 编程,一个服务器,多个客户端,互相通信
热 ★★★
Socket 编程,一个服务器,多个客户端,互相通信
作者:闵涛 文章来源:闵涛的学习笔记 点击数:2661 更新时间:2011/11/20 12:49:27
我只能给大家一个很简单的Socket的初级通信. 给大家做一个小的服务器,刚刚好前段时间做了一个小的聊天程序,实现了: 指定客户端发送消息,发送闪屏,支持服务器监听客户端发送消息 具体的代码如下: 首先是服务器.
以下是代码片段: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Net; // Endpoint using System.Net.Sockets; // 包含套接字 using System.Text; using System.Windows.Forms; using System.Threading; using System.IO; namespace Server { public partial class Form1 : Form { public Form1() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false ; // 关闭跨线程修改控件检查 } Socket sokWatch = null ; // 负责监听 客户端段 连接请求的 套接字(女生宿舍的大妈) Thread threadWatch = null ; // 负责 调用套接字, 执行 监听请求的线程 // 开启监听 按钮 private void btnStartListen_Click( object sender, EventArgs e) { // 实例化 套接字 (ip4寻址协议,流式传输,TCP协议) sokWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建 ip对象 IP Address address = IP Address.Parse(txtIP.Text.Trim()); // 创建网络节点对象 包含 ip和port IPEndPoint endpoint = new IPEndPoint(address, int .Parse(txtPort.Text.Trim())); // 将 监听套接字 绑定到 对应的IP和端口 sokWatch.Bind(endpoint); // 设置 监听队列 长度为10(同时能够处理 10个连接请求) sokWatch.Listen( 10 ); threadWatch = new Thread(StartWatch); threadWatch.IsBackground = true ; threadWatch.Start(); txtShow.AppendText( " 启动服务器成功......\r\n " ); } // Dictionary<string, Socket> dictSocket = new Dictionary<string, Socket>(); Dictionary < string , ConnectionClient > dictConn = new Dictionary < string , ConnectionClient > (); bool isWatch = true ; #region 1.被线程调用 监听连接端口 /// <summary> /// 被线程调用 监听连接端口 /// </summary> void StartWatch() { while (isWatch) { // threadWatch.SetApartmentState(ApartmentState.STA); // 监听 客户端 连接请求,但是,Accept会阻断当前线程 Socket sokMsg = sokWatch.Accept(); // 监听到请求,立即创建负责与该客户端套接字通信的套接字 ConnectionClient connection = new ConnectionClient(sokMsg, ShowMsg, RemoveClientConnection); // 将负责与当前连接请求客户端 通信的套接字所在的连接通信类 对象 装入集合 dictConn.Add(sokMsg.RemoteEndPoint.ToString(), connection); // 将 通信套接字 加入 集合,并以通信套接字的远程IpPort作为键 // dictSocket.Add(sokMsg.RemoteEndPoint.ToString(), sokMsg); // 将 通信套接字的 客户端IP端口保存在下拉框里 cboClient.Items.Add(sokMsg.RemoteEndPoint.ToString()); ShowMsg( " 接收连接成功...... " ); // 启动一个新线程,负责监听该客户端发来的数据 // Thread threadConnection = new Thread(ReciveMsg); // threadConnection.IsBackground = true; // threadConnection.Start(sokMsg); } } #endregion bool isRec = true ; // 与客户端通信的套接字 是否 监听消息 #region 发送消息 到指定的客户端 -btnSend_Click // 发送消息 到指定的客户端 private void btnSend_Click( object sender, EventArgs e) { // byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(txtInput.Text.Trim()); // 从下拉框中 获得 要哪个客户端发送数据 string connectionSokKey = cboClient.Text; if ( ! string .IsNullOrEmpty(connectionSokKey)) { // 从字典集合中根据键获得 负责与该客户端通信的套接字,并调用send方法发送数据过去 dictConn[connectionSokKey].Send(txtInput.Text.Trim()); // sokMsg.Send(arrMsg); } else { MessageBox.Show(" 请选择要发送的客户端~~ " ); } } #endregion // 发送闪屏!! private void btnShack_Click( object sender, EventArgs e) { string connectionSokKey = cboClient.Text; if ( ! string .IsNullOrEmpty(connectionSokKey)) { dictConn[connectionSokKey].SendShake(); } else { MessageBox.Show(" 请选择要发送的客户端~~ " ); } } // 群闪 private void btnShackAll_Click( object sender, EventArgs e) { foreach (ConnectionClient conn in dictConn.Values) { conn.SendShake(); } } #region 2 移除与指定客户端的连接 +void RemoveClientConnection(string key) /// <summary> /// 移除与指定客户端的连接 /// </summary> /// <param name="key"> 指定客户端的IP和端口 </param> public void RemoveClientConnection( string key) { if (dictConn.ContainsKey(key)) { dictConn.Remove(key); cboClient.Items.Remove(key); } } #endregion // 选择要发送的文件 private void btnChooseFile_Click( object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { txtFilePath.Text = ofd.FileName; } } // 发送文件 private void btnSendFile_Click( object sender, EventArgs e) { // 拿到下拉框中选中的客户端IPPORT string key = cboClient.Text; if ( ! string .IsNullOrEmpty(key)) { dictConn[key].SendFile(txtFilePath.Text.Trim()); } } #region 向文本框显示消息 -void ShowMsg(string msgStr) /// <summary> /// 向文本框显示消息 /// </summary> /// <param name="msgStr"> 消息 </param> public void ShowMsg( string msgStr) { txtShow.AppendText(msgStr + " \r\n " ); } #endregion private void btnSendMsgAll_Click( object sender, EventArgs e) { foreach (ConnectionClient conn in dictConn.Values) { conn.Send(txtInput.Text.Trim()); } } } }
在这里,我新建了一个与客户端的通信和线程的类(ConnectionClient).
以下是代码片段: using System; using System.Collections.Generic; using System.Threading; using System.Net; using System.Net.Sockets; using System.Text; using System.IO; namespace Server { /// <summary> /// 与客户端的 连接通信类(包含了一个 与客户端 通信的 套接字,和线程) /// </summary> public class ConnectionClient { Socket sokMsg; DGShowMsg dgShowMsg; // 负责 向主窗体文本框显示消息的方法委托 DGShowMsg dgRemoveConnection; // 负责 从主窗体 中移除 当前连接 Thread threadMsg; #region 构造函数 /// <summary> /// /// </summary> /// <param name="sokMsg"> 通信套接字 </param> /// <param name="dgShowMsg"> 向主窗体文本框显示消息的方法委托 </param> public ConnectionClient(Socket sokMsg, DGShowMsg dgShowMsg, DGShowMsg dgRemoveConnection) { this .sokMsg = sokMsg; this .dgShowMsg = dgShowMsg; this .dgRemoveConnection = dgRemoveConnection; this .threadMsg = new Thread(RecMsg); this .threadMsg.IsBackground = true ; this .threadMsg.Start(); } #endregion bool isRec = true ; #region 02负责监听客户端发送来的消息 void RecMsg() { while (isRec) { try { byte [] arrMsg = new byte [ 1024 * 1024 * 2 ]; // 接收 对应 客户端发来的消息 int length = sokMsg.Receive(arrMsg); // 将接收到的消息数组里真实消息转成字符串 string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, 0 , length); // 通过委托 显示消息到 窗体的文本框 dgShowMsg(strMsg); } catch (Exception ex) { isRec = false ; // 从主窗体中 移除 下拉框中对应的客户端选择项,同时 移除 集合中对应的 ConnectionClient对象 dgRemoveConnection(sokMsg.RemoteEndPoint.ToString()); } } } #endregion #region 03向客户端发送消息 /// <summary> /// 向客户端发送消息 /// </summary> /// <param name="strMsg"></param> public void Send( string strMsg) { byte [] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); byte [] arrMsgFinal = new byte [arrMsg.Length + 1 ]; arrMsgFinal[ 0 ] = 0 ; // 设置 数据标识位等于0,代表 发送的是 文字 arrMsg.CopyTo(arrMsgFinal, 1 ); sokMsg.Send(arrMsgFinal); } #endregion #region 04向客户端发送文件数据 +void SendFile(string strPath) /// <summary> /// 04向客户端发送文件数据 /// </summary> /// <param name="strPath"> 文件路径 </param> public void SendFile( string strPath) { // 通过文件流 读取文件内容 using (FileStream fs = new FileStream(strPath, FileMode.OpenOrCreate)) { byte [] arrFile = new byte [ 1024 * 1024 * 2 ]; // 读取文件内容到字节数组,并 获得 实际文件大小 int length = fs.Read(arrFile, 0 , arrFile.Length); // 定义一个 新数组,长度为文件实际长度 +1 byte [] arrFileFina = new byte [length + 1 ]; arrFileFina[ 0 ] = 1 ; // 设置 数据标识位等于1,代表 发送的是文件 // 将 文件数据数组 复制到 新数组中,下标从1开始 // arrFile.CopyTo(arrFileFina, 1); Buffer.BlockCopy(arrFile, 0 , arrFileFina, 1 , length); // 发送文件数据 sokMsg.Send(arrFileFina); // , 0, length + 1, SocketFlags.None); } } #endregion #region 05向客户端发送闪屏 /// <summary> /// 向客户端发送闪屏 /// </summary> /// <param name="strMsg"></param> public void SendShake() { byte [] arrMsgFinal = new byte [ 1 ]; arrMsgFinal[ 0 ] = 2 ; sokMsg.Send(arrMsgFinal); } #endregion #region 06关闭与客户端连接 /// <summary> /// 关闭与客户端连接 /// </summary> public void CloseConnection() { isRec = false ; } #endregion } }
万事俱备只欠客户端.
以下是代码片段: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Net.Sockets; using System.Net; using System.Threading; using System.Windows.Forms; using System.IO; namespace Client { public partial class Form1 : Form { public Form1() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false ; } Socket sokClient = null ; // 负责与服务端通信的套接字 Thread threadClient = null ; // 负责 监听 服务端发送来的消息的线程 bool isRec = true ; // 是否循环接收服务端数据 private void btnConnect_Click( object sender, EventArgs e) { // 实例化 套接字 sokClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建 ip对象 IPAddress address = IPAddress.Parse(txtIP.Text.Trim()); // 创建网络节点对象 包含 ip和port IPEndPoint endpoint = new IPEndPoint(address, int .Parse(txtPort.Text.Trim())); // 连接 服务端监听套接字 sokClient.Connect(endpoint); // 创建负责接收 服务端发送来数据的 线程 threadClient = new Thread(ReceiveMsg); threadClient.IsBackground = true ; // 如果在win7下要通过 某个线程 来调用 文件选择框的代码,就需要设置如下 threadClient.SetApartmentState(ApartmentState.STA); threadClient.Start(); } /// <summary> /// 接收服务端发送来的消息数据 /// </summary> void ReceiveMsg() { while (isRec) { byte [] msgArr = new byte [ 1024 * 1024 * 1 ]; // 接收到的消息的缓冲区 int length = 0 ; // 接收服务端发送来的消息数据 length = sokClient.Receive(msgArr); // Receive会阻断线程 if (msgArr[ 0 ] == 0 ) // 发送来的是文字 { string strMsg = System.Text.Encoding.UTF8.GetString(msgArr, 1 , length - 1 ); txtShow.AppendText(strMsg + " \r\n " ); } else if (msgArr[ 0 ] == 1 ) { // 发送来的是文件 SaveFileDialog sfd = new SaveFileDialog(); // 弹出文件保存选择框 if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { // 创建文件流 using (FileStream fs = new FileStream(sfd.FileName, FileMode.OpenOrCreate)) { fs.Write(msgArr, 1 , length - 1 ); MessageBox.Show( " 文件保存成功! " ); } } } else if (msgArr[ 0 ] == 2 ) { ShakeWindow(); } } } /// <summary> /// 闪屏 /// </summary> private void ShakeWindow() { Random ran = new Random(); // 保存 窗体原坐标 System.Drawing.Point point = this .Location; for ( int i = 0 ; i < 30 ; i ++ ) { // 随机 坐标 this .Location = new System.Drawing.Point(point.X + ran.Next( 8 ), point.Y + ran.Next( 8 )); System.Threading.Thread.Sleep( 15 ); // 休息15毫秒 this .Location = point; // 还原 原坐标(窗体回到原坐标) System.Threading.Thread.Sleep( 15 ); // 休息15毫秒 } } // 发送消息 private void btnSend_Click( object sender, EventArgs e) { byte [] arrMsg = System.Text.Encoding.UTF8.GetBytes(txtInput.Text.Trim()); sokClient.Send(arrMsg); } } }
这上面的代码,就能实现Socket通信,可以实现聊天. 希望能对大家有点帮助! 由于代码有点长.分段发的...
怎么解决客户端或服务器端Socket.Close()的时候,另一段出现的异常?谢谢! 专门去定义一个处理异常的方法
以下是代码片段: // 移除与指定客户端的连接 // key 指定客户端的IP和端口 public void RemoveClient( string key) { if (conn.ContainsKey(key)) { // 删除这个客户端 conn.Remove(key); // 看看是否有这个客户端. cboClient.Items.Remove(key); } }
就是需要去判断一下这个客户端是否包含这个key 如果包含,那么则移除掉这个key,然后把combox控件上面的这个客户端移除! [网络技术 ]PPTP服务器的端口 [聊天工具 ]Office 2000 服务器扩展 [聊天工具 ]Firefox小插件gspace把Gmail变FTP服务器 [系统软件 ]突破Windows 2003 PHP服务器的新思路 [常用软件 ]微软最新VoIP服务器及客户端软件下周开测 [常用软件 ]uTorrent:史上最省资源BT客户端试用 [常用软件 ]越看越流畅三款主流网络电视客户端导购 [常用软件 ]Allpeers:让Firefox摇身一变为P2P客户端 [常用软件 ]编译给自己专用的FTP客户端 [常用软件 ][网络]FTPRush FTP客户端 软件评测
教程录入:mintao 责任编辑:mintao
上一篇教程: C#网络编程客户端程序实现源码分享 下一篇教程: 利用c#和webbrowser轻松打造注册机和回帖机
【字体:小 大 】【发表评论 】【加入收藏 】【告诉好友 】【打印此文 】【关闭窗口 】
注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
网友评论: (只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
同类栏目
赞助链接
500 - 内部服务器错误。
500 - 内部服务器错误。
您查找的资源存在问题,因而无法显示。