服务器端:

  1. using System;
  2. using System.Windows.Forms;
  3. using System.Net.Sockets;
  4. using System.Net;//IPAddress,IPEndPoint(ip和端口)类
  5. using System.Threading;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. namespace MyChatRoomServer
  9. {
  10. public partial class Server : Form
  11. {
  12. public Server()
  13. {
  14. InitializeComponent();
  15. //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查
  16. TextBox.CheckForIllegalCrossThreadCalls = false;
  17. }
  18. Thread threadWatch = null;//负责监听客户端连接请求的线程
  19. Socket socketWatch = null;//负责监听的套接字
  20. private void btnBeginListen_Click(object sender, EventArgs e)
  21. {
  22. //创建服务端负责监听的套接字,参数(IPV4寻址协议,使用流格式,使用Tcp传输协议)
  23. socketWatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
  24. //获得文本框中IP地址对象
  25. IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
  26. //获取端口号
  27. IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
  28. //将负责监听的套接字绑定到唯一的IP和端口上
  29. socketWatch.Bind(endpoint);
  30. //设置监听队列的长度,意思就是同时发送请求只能10个
  31. socketWatch.Listen(10);
  32. //创建一个新的套接字专门负责跟客户端通信,注意:Accept方法会阻塞当前程序
  33. //Socket sockConnection = socketWatch.Accept();
  34. //创建负责监听的线程,并传入监听的方法
  35. threadWatch = new Thread(WatchConnecting);
  36. threadWatch.IsBackground = true;//设置为后台线程,只要前台的一结束,那么程序就结束
  37. threadWatch.Start();//启动线程
  38. ShowMsg("服务器启动监听成功!");
  39. }
  40. //保存了服务器端所有负责和客户端通信的套接字
  41. Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
  42. //保存了服务器端所有负责调用通信套接字Recive方法的线程
  43. Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
  44. //Socket sokConnection = null;//负责通信的套接字
  45. /// <summary>
  46. /// 监听客户端请求的方法
  47. /// </summary>
  48. void WatchConnecting()
  49. {
  50. while (true) //加上循环持续不断的监听新的客户端的连接
  51. {
  52. //开始监听客户端连接请求,注意:Accept方法会阻断当时线程
  53. Socket sokConnection = socketWatch.Accept();
  54. //将列表控件中追加一个客户端的ip端口字符串,作为客户端的唯一标识
  55. lblOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
  56. //将与客户端通信的套接字对象sokConnection添加到键值对集合中,并以客户端IP端口作为键
  57. dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
  58. //创建一个委托
  59. ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);
  60. //创建通信线程
  61. Thread thr = new Thread(pts);
  62. thr.IsBackground = true; //设置为后台
  63. thr.Start(sokConnection); //将线程的参数传入
  64. dictThread.Add(sokConnection.RemoteEndPoint.ToString(),thr);
  65. ShowMsg("客户端连接成功!"+sokConnection.RemoteEndPoint.ToString());
  66. }
  67. }
  68. /// <summary>
  69. /// 服务端负责监听客户端发来的数据方法
  70. /// </summary>
  71. void RecMsg(object socketClientPara)
  72. {
  73. Socket socketClient = socketClientPara as Socket;
  74. while (true)
  75. {
  76. //定义一个2M的接受数据的缓存区
  77. byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间
  78. //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度
  79. int length = socketClient.Receive(arrMsgRec);
  80. try
  81. {
  82. length = socketClient.Receive(arrMsgRec);
  83. }
  84. catch (SocketException ex)
  85. {
  86. ShowMsg("异常" + ex.Message+",RemoteEnd=" + socketClient.RemoteEndPoint.ToString());
  87. //从通信套接字集合中删除被中断连接的通信套接字
  88. dict.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的端口和IP
  89. //从通信线程中删除被中断的连接通信线程对象
  90. dictThread.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的线程
  91. //有异常则跳出,不执行后面的
  92. //从列表中删除被中断的连接IP:port
  93. lblOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
  94. break;
  95. //return;
  96. }
  97. catch (Exception ex)
  98. {
  99. ShowMsg("异常" + ex.Message);
  100. //有异常则跳出,不执行后面的
  101. return;
  102. }
  103. if (arrMsgRec[0] == 0)//判断传过来的第一个数据是0,则代表是文本数据
  104. {
  105. //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符
  106. string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
  107. ShowMsg(strMsgRec);
  108. }
  109. //如果是1,则代表发送过来的是文件数据(文件/图片...)
  110. else if (arrMsgRec[0] == 1)
  111. {
  112. //保存文件选择框对象
  113. SaveFileDialog sfd = new SaveFileDialog();
  114. if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  115. {
  116. string fileSavePath = sfd.FileName;//获得文件保存路径
  117. //创建文件流,让文件流来根据路径创建一个文件
  118. using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
  119. {
  120. fs.Write(arrMsgRec,1,length-1);
  121. ShowMsg("文件保存成功:"+fileSavePath);
  122. }
  123. }
  124. }
  125. }
  126. }
  127. void ShowMsg(string msg)
  128. {
  129. txtMsg.AppendText(msg + "\r\n");
  130. }
  131. private void btnCloseServer_Click(object sender, EventArgs e)
  132. {
  133. //threadWatch.Abort();//关闭线程
  134. //ShowMsg("服务器启动监听成功!");
  135. }
  136. //发送消息到客户端
  137. private void btnSend_Click(object sender, EventArgs e)
  138. {
  139. if (string.IsNullOrEmpty(lblOnline.Text))
  140. {
  141. MessageBox.Show("请在左侧选择要发送的好友");
  142. }
  143. else
  144. {
  145. string strMsg = txtMsgSend.Text.Trim();
  146. //将要发送的字符串转成utf8对应的字节数组
  147. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
  148. string strClientKey = lblOnline.Text;
  149. //通过key找到字典集合中对应的与某个用户客户端通信的套接字的send方法,发数据给对方
  150. try
  151. {
  152. dict[strClientKey].Send(arrMsg);
  153. //一旦上面的出现异常,下面的就不执行
  154. ShowMsg("发送了数据出去:" + strMsg);
  155. }
  156. //sokConnection.Send(arrMsg);
  157. catch (SocketException ex)
  158. {
  159. ShowMsg("发送时异常:"+ex.Message);
  160. return;
  161. }
  162. catch (Exception ex)
  163. {
  164. ShowMsg("发送时异常:" + ex.Message);
  165. return;
  166. }
  167. }
  168. }
  169. //服务器群发消息
  170. private void btnSendToAll_Click(object sender, EventArgs e)
  171. {
  172. string strMsg = txtMsgSend.Text.Trim();
  173. //将要发送的字符串转成utf8对应的字节数组
  174. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
  175. //便利当前字典里面所有的通信套接字
  176. foreach (Socket s in dict.Values)
  177. {
  178. s.Send(arrMsg);
  179. }
  180. ShowMsg("群发完毕!:)");
  181. }
  182. }
  183. }

客户端:

    1. using System;
    2. using System.Windows.Forms;
    3. using System.Net;
    4. using System.Net.Sockets;
    5. using System.Threading;
    6. using System.IO;
    7. namespace MyChatRoomClient
    8. {
    9. public partial class FChatClient : Form
    10. {
    11. public FChatClient()
    12. {
    13. InitializeComponent();
    14. //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查
    15. //关闭跨线程检查
    16. TextBox.CheckForIllegalCrossThreadCalls = false;
    17. }
    18. IPAddress address = null;
    19. IPEndPoint endpoint = null;
    20. Socket socketClient = null; //客户端套接字
    21. Thread threadClient = null;//客户端负责接受服务端发来的消息的线程
    22. //客户端发送请求到服务器
    23. private void btnConnect_Click(object sender, EventArgs e)
    24. {
    25. address = IPAddress.Parse(txtIP.Text.Trim());
    26. endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
    27. socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    28. try
    29. {
    30. socketClient.Connect(endpoint);
    31. //创建线程,监听服务器端发来的消息
    32. threadClient = new Thread(RecMsg);
    33. threadClient.IsBackground = true;
    34. threadClient.Start();
    35. }
    36. catch (Exception ee)
    37. {
    38. MessageBox.Show(ee.ToString());
    39. }
    40. }
    41. /// <summary>
    42. /// 监听服务器端发来的消息
    43. /// </summary>
    44. void RecMsg()
    45. {
    46. while (true)
    47. {
    48. //定义一个2M的接受数据的缓存区
    49. byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间
    50. //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度
    51. int length = socketClient.Receive(arrMsgRec);
    52. //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符
    53. string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);
    54. ShowMsg(strMsgRec);
    55. }
    56. }
    57. #region 在窗体文本框中显示消息-void ShowMsg(string msg)
    58. /// <summary>
    59. /// 在窗体文本框中显示消息
    60. /// </summary>
    61. /// <param name="msg">消息</param>
    62. void ShowMsg(string msg)
    63. {
    64. txtMsg.AppendText(msg + "\r\n");
    65. }
    66. #endregion
    67. #region 选择要发送的文件-btnChooseFile_Click
    68. //选择要发送的文件
    69. private void btnChooseFile_Click(object sender, EventArgs e)
    70. {
    71. OpenFileDialog ofd = new OpenFileDialog();
    72. if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    73. {
    74. txtFilePath.Text = ofd.FileName;
    75. }
    76. }
    77. #endregion
    78. //向服务器发送文本消息
    79. private void btnSendMsg_Click(object sender, EventArgs e)
    80. {
    81. string strMsg = txtMsgSend.Text.Trim();
    82. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
    83. byte[] arrMsgSend = new byte[arrMsg.Length + 1];
    84. arrMsgSend[0] = 0;//设置标识位,0代表是文字
    85. Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);
    86. socketClient.Send(arrMsgSend);
    87. ShowMsg("我发送了:" + strMsg);
    88. }
    89. //向服务端发送文件
    90. private void btnSendFile_Click(object sender, EventArgs e)
    91. {
    92. //用文件流打开用户选择的文件
    93. using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))
    94. {
    95. byte[] arrFile = new byte[1024*1024*2];//定义一个2M缓存区
    96. //将文件数据读到数组arrFile中,并获得读取的真是数据长度
    97. int length = fs.Read(arrFile,0,arrFile.Length);
    98. byte[] arrFileSend = new byte[length + 1];
    99. arrFileSend[0] = 1;//代表发送的是文件数据
    100. //for (int i = 0; i < length; i++)
    101. //{
    102. //    arrFileSend[i + 1] = arrFile[i];
    103. //}
    104. //数据块的拷贝,将arrFile从第0个开始拷贝,拷贝到arrFileSend,从第一个开始存放
    105. Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);
    106. //arrFile.CopyTo(arrFileSend,length);只能从0开始拷贝
    107. //发送了包含了标识位的新数据到服务端
    108. socketClient.Send(arrFileSend);
    109. }
    110. }
    111. }
    112. }

最新文章

  1. 20个JS优化代码技巧
  2. viewpaper
  3. node-gyp rebuild 卡住?
  4. Javascript操作剪切板数据(支持IE、Chrome、360、搜狗),亲测!
  5. 如何精确地测量java对象的大小-底层instrument API
  6. tomcat作为windows服务启动失败解决方法
  7. Canny边缘检測算法原理及其VC实现具体解释(一)
  8. IOS中 类扩展 xib
  9. IP核之初——FIFO添加以太网MAC头部
  10. 【转载】Apache Spark Jobs 性能调优(一)
  11. Lintcode393 Best Time to Buy and Sell Stock IV solution 题解
  12. PAT1086:Tree Traversals Again
  13. Spring Cloud Eureka Server高可用注册服务中心的配置
  14. python列表中的所有值转换为字符串,以及列表拼接成一个字符串
  15. More Effective C# 【前戏】
  16. iOS ReplayKit实时录制屏幕实现方案的细节记录
  17. .net基本面试题
  18. 【机器学习_5】Anaconda:初学Python、入门机器学习的首选
  19. python3.6 使用pyinstaller 打包web程序的方法
  20. MD5 算法

热门文章

  1. splice 很好用
  2. .net Framework Class Library(FCL)
  3. HDU 4940 Destroy Transportation system(无源汇有上下界最大流)
  4. nyoj305_表达式求值
  5. jquery格式化时间
  6. php dirname($path) 中文路径不对问题
  7. springMVC创建基础变量
  8. UVa1593_Allgnment_Of_Code
  9. js函数动态传参
  10. hadoop+javaWeb的开发中遇到包冲突问题(java.lang.VerifyError)