C#中TCP实现多个客户端与服务端数据与文件的传输

下面是我用C#写的 一个简单的TCP通信,主要的功能有:

(1) 多个客户端与服务器间的数据交流

(2)可以实现群发的功能

(3)客户端与服务端可以进行文件的传输

主要用到的知识: TCP里的 socket 、、、 多线程 Thread 、、、

下面的是界面:【成都c++培训哪里好



下面分别是服务端和客户端的代码,如若借用,请标明出处~~~

服务端代码:

[csharp] view plaincopyprint?
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.Net.Sockets;
  10. using System.Net; // IP,IPAddress, IPEndPoint,端口等;
  11. using System.Threading;
  12. using System.IO;
  13. namespace _11111
  14. {
  15. public partial class frm_server : Form
  16. {
  17. public frm_server()
  18. {
  19. InitializeComponent();
  20. TextBox.CheckForIllegalCrossThreadCalls = false;
  21. }
  22. Thread threadWatch = null; // 负责监听客户端连接请求的 线程;
  23. Socket socketWatch = null;
  24. Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
  25. Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
  26. private void btnBeginListen_Click(object sender, EventArgs e)
  27. {
  28. // 创建负责监听的套接字,注意其中的参数;
  29. socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  30. // 获得文本框中的IP对象;
  31. IPAddress address = IPAddress.Parse(txtIp.Text.Trim());
  32. // 创建包含ip和端口号的网络节点对象;
  33. IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
  34. try
  35. {
  36. // 将负责监听的套接字绑定到唯一的ip和端口上;
  37. socketWatch.Bind(endPoint);
  38. }
  39. catch (SocketException se)
  40. {
  41. MessageBox.Show("异常:"+se.Message);
  42. return;
  43. }
  44. // 设置监听队列的长度;
  45. socketWatch.Listen(10);
  46. // 创建负责监听的线程;
  47. threadWatch = new Thread(WatchConnecting);
  48. threadWatch.IsBackground = true;
  49. threadWatch.Start();
  50. ShowMsg("服务器启动监听成功!");
  51. //}
  52. }
  53. /// <summary>
  54. /// 监听客户端请求的方法;
  55. /// </summary>
  56. void WatchConnecting()
  57. {
  58. while (true) // 持续不断的监听客户端的连接请求;
  59. {
  60. // 开始监听客户端连接请求,Accept方法会阻断当前的线程;
  61. Socket sokConnection = socketWatch.Accept(); // 一旦监听到一个客户端的请求,就返回一个与该客户端通信的 套接字;
  62. // 想列表控件中添加客户端的IP信息;
  63. lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
  64. // 将与客户端连接的 套接字 对象添加到集合中;
  65. dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
  66. ShowMsg("客户端连接成功!");
  67. Thread thr = new Thread(RecMsg);
  68. thr.IsBackground = true;
  69. thr.Start(sokConnection);
  70. dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr); // 将新建的线程 添加 到线程的集合中去。
  71. }
  72. }
  73. void RecMsg(object sokConnectionparn)
  74. {
  75. Socket sokClient = sokConnectionparn as Socket;
  76. while (true)
  77. {
  78. // 定义一个2M的缓存区;
  79. byte[] arrMsgRec = new byte[1024 * 1024 * 2];
  80. // 将接受到的数据存入到输入 arrMsgRec中;
  81. int length = -1;
  82. try
  83. {
  84. length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
  85. }
  86. catch (SocketException se)
  87. {
  88. ShowMsg("异常:" + se.Message);
  89. // 从 通信套接字 集合中删除被中断连接的通信套接字;
  90. dict.Remove(sokClient.RemoteEndPoint.ToString());
  91. // 从通信线程集合中删除被中断连接的通信线程对象;
  92. dictThread.Remove(sokClient.RemoteEndPoint.ToString());
  93. // 从列表中移除被中断的连接IP
  94. lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
  95. break;
  96. }
  97. catch (Exception e)
  98. {
  99. ShowMsg("异常:" + e.Message);
  100. // 从 通信套接字 集合中删除被中断连接的通信套接字;
  101. dict.Remove(sokClient.RemoteEndPoint.ToString());
  102. // 从通信线程集合中删除被中断连接的通信线程对象;
  103. dictThread.Remove(sokClient.RemoteEndPoint.ToString());
  104. // 从列表中移除被中断的连接IP
  105. lbOnline.Items.Remove(sokClient.RemoteEndPoint.ToString());
  106. break;
  107. }
  108. if (arrMsgRec[0] == 0) // 表示接收到的是数据;
  109. {
  110. string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec,1, length-1);// 将接受到的字节数据转化成字符串;
  111. ShowMsg(strMsg);
  112. }
  113. if (arrMsgRec[0] == 1) // 表示接收到的是文件;
  114. {
  115. SaveFileDialog sfd = new SaveFileDialog();
  116. if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
  117. {// 在上边的 sfd.ShowDialog() 的括号里边一定要加上 this 否则就不会弹出 另存为 的对话框,而弹出的是本类的其他窗口,,这个一定要注意!!!【解释:加了this的sfd.ShowDialog(this),“另存为”窗口的指针才能被SaveFileDialog的对象调用,若不加thisSaveFileDialog 的对象调用的是本类的其他窗口了,当然不弹出“另存为”窗口。】 【成都c++培训哪里好
  118. string fileSavePath = sfd.FileName;// 获得文件保存的路径;
  119. // 创建文件流,然后根据路径创建文件;
  120. using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
  121. {
  122. fs.Write(arrMsgRec, 1, length - 1);
  123. ShowMsg("文件保存成功:" + fileSavePath);
  124. }
  125. }
  126. }
  127. }
  128. }
  129. void ShowMsg(string str)
  130. {
  131. txtMsg.AppendText(str + "\r\n");
  132. }
  133. // 发送消息
  134. private void btnSend_Click(object sender, EventArgs e)
  135. {
  136. string strMsg = "服务器" + "\r\n" + " -->" + txtMsgSend.Text.Trim() + "\r\n";
  137. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
  138. byte[] arrSendMsg=new byte[arrMsg.Length+1];
  139. arrSendMsg[0] = 0; // 表示发送的是消息数据
  140. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
  141. string strKey = "";
  142. strKey = lbOnline.Text.Trim();
  143. if (string.IsNullOrEmpty(strKey)) // 判断是不是选择了发送的对象;
  144. {
  145. MessageBox.Show("请选择你要发送的好友!!!");
  146. }
  147. else
  148. {
  149. dict[strKey].Send(arrSendMsg);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
  150. ShowMsg(strMsg);
  151. txtMsgSend.Clear();
  152. }
  153. }
  154. /// <summary>
  155. /// 群发消息
  156. /// </summary>
  157. /// <param name="sender"></param>
  158. /// <param name="e">消息</param>
  159. private void btnSendToAll_Click(object sender, EventArgs e)
  160. {
  161. string strMsg = "服务器" + "\r\n" + " -->" + txtMsgSend.Text.Trim() + "\r\n";
  162. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
  163. foreach (Socket s in dict.Values)
  164. {
  165. s.Send(arrMsg);
  166. }
  167. ShowMsg(strMsg);
  168. txtMsgSend.Clear();
  169. ShowMsg(" 群发完毕~~~");
  170. }
  171. // 选择要发送的文件
  172. private void btnSelectFile_Click_1(object sender, EventArgs e)
  173. {
  174. OpenFileDialog ofd = new OpenFileDialog();
  175. ofd.InitialDirectory = "D:\\";
  176. if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  177. {
  178. txtSelectFile.Text = ofd.FileName;
  179. }
  180. }
  181. // 文件的发送
  182. private void btnSendFile_Click_1(object sender, EventArgs e)
  183. {
  184. if (string.IsNullOrEmpty(txtSelectFile.Text))
  185. {
  186. MessageBox.Show("请选择你要发送的文件!!!");
  187. }
  188. else
  189. {
  190. // 用文件流打开用户要发送的文件;
  191. using (FileStream fs = new FileStream(txtSelectFile.Text, FileMode.Open))
  192. {
  193. string fileName=System.IO.Path.GetFileName(txtSelectFile.Text);
  194. string fileExtension=System.IO.Path.GetExtension(txtSelectFile.Text);
  195. string strMsg = "我给你发送的文件为: "+fileName+fileExtension+"\r\n";
  196. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
  197. byte[] arrSendMsg = new byte[arrMsg.Length + 1];
  198. arrSendMsg[0] = 0; // 表示发送的是消息数据
  199. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
  200. bool fff = true;
  201. string strKey = "";
  202. strKey = lbOnline.Text.Trim();
  203. if (string.IsNullOrEmpty(strKey)) // 判断是不是选择了发送的对象;
  204. {
  205. MessageBox.Show("请选择你要发送的好友!!!");
  206. }
  207. else
  208. {
  209. dict[strKey].Send(arrSendMsg);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
  210. byte[] arrFile = new byte[1024 * 1024 * 2];
  211. int length = fs.Read(arrFile, 0, arrFile.Length); // 将文件中的数据读到arrFile数组中;
  212. byte[] arrFileSend = new byte[length + 1];
  213. arrFileSend[0] = 1; // 用来表示发送的是文件数据;
  214. Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length);
  215. // 还有一个 CopyTo的方法,但是在这里不适合; 当然还可以用for循环自己转化;
  216. // sockClient.Send(arrFileSend);// 发送数据到服务端;
  217. dict[strKey].Send(arrFileSend);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
  218. txtSelectFile.Clear();
  219. }
  220. }
  221. }
  222. txtSelectFile.Clear();
  223. }
  224. }
  225. }


客户端代码: 【成都c++培训哪里好

[csharp] view plaincopyprint?
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Threading;
  12. using System.IO;
  13. namespace _2222222
  14. {
  15. public partial class frmClient : Form
  16. {
  17. public frmClient()
  18. {
  19. InitializeComponent();
  20. TextBox.CheckForIllegalCrossThreadCalls = false;
  21. }
  22. Thread threadClient = null; // 创建用于接收服务端消息的 线程;
  23. Socket sockClient = null;
  24. private void btnConnect_Click(object sender, EventArgs e)
  25. {
  26. IPAddress ip = IPAddress.Parse(txtIp.Text.Trim());
  27. IPEndPoint endPoint=new IPEndPoint (ip,int.Parse(txtPort.Text.Trim()));
  28. sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  29. try
  30. {
  31. ShowMsg("与服务器连接中……");
  32. sockClient.Connect(endPoint);
  33. }
  34. catch (SocketException se)
  35. {
  36. MessageBox.Show(se.Message);
  37. return;
  38. //this.Close();
  39. }
  40. ShowMsg("与服务器连接成功!!!");
  41. threadClient = new Thread(RecMsg);
  42. threadClient.IsBackground = true;
  43. threadClient.Start();
  44. }
  45. void RecMsg()
  46. {
  47. while (true)
  48. {
  49. // 定义一个2M的缓存区;
  50. byte[] arrMsgRec = new byte[1024 * 1024 * 2];
  51. // 将接受到的数据存入到输入 arrMsgRec中;
  52. int length = -1;
  53. try
  54. {
  55. length = sockClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
  56. }
  57. catch (SocketException se)
  58. {
  59. ShowMsg("异常;" + se.Message);
  60. return;
  61. }
  62. catch (Exception e)
  63. {
  64. ShowMsg("异常:"+e.Message);
  65. return;
  66. }
  67. if (arrMsgRec[0] == 0) // 表示接收到的是消息数据;
  68. {
  69. string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);// 将接受到的字节数据转化成字符串;
  70. ShowMsg(strMsg);
  71. }
  72. if (arrMsgRec[0] == 1) // 表示接收到的是文件数据;
  73. {
  74. try
  75. {
  76. SaveFileDialog sfd = new SaveFileDialog();
  77. if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
  78. {// 在上边的 sfd.ShowDialog() 的括号里边一定要加上 this 否则就不会弹出 另存为 的对话框,而弹出的是本类的其他窗口,,这个一定要注意!!!【解释:加了this的sfd.ShowDialog(this),“另存为”窗口的指针才能被SaveFileDialog的对象调用,若不加thisSaveFileDialog 的对象调用的是本类的其他窗口了,当然不弹出“另存为”窗口。】
  79. string fileSavePath = sfd.FileName;// 获得文件保存的路径;
  80. // 创建文件流,然后根据路径创建文件;
  81. using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
  82. {
  83. fs.Write(arrMsgRec, 1, length - 1);
  84. ShowMsg("文件保存成功:" + fileSavePath);
  85. }
  86. }
  87. }
  88. catch (Exception aaa)
  89. {
  90. MessageBox.Show(aaa.Message);
  91. }
  92. }
  93. }
  94. }
  95. void ShowMsg(string str)
  96. {
  97. txtMsg.AppendText(str + "\r\n");
  98. }
  99. // 发送消息;
  100. private void btnSendMsg_Click(object sender, EventArgs e)
  101. {
  102. string strMsg = txtName.Text.Trim()+"\r\n"+" -->"+ txtSendMsg.Text.Trim()+ "\r\n";
  103. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
  104. byte[] arrSendMsg = new byte[arrMsg.Length + 1];
  105. arrSendMsg[0] = 0; // 用来表示发送的是消息数据
  106. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
  107. sockClient.Send(arrSendMsg); // 发送消息;
  108. ShowMsg(strMsg);
  109. txtSendMsg.Clear();
  110. }
  111. // 选择要发送的文件;
  112. private void btnSelectFile_Click(object sender, EventArgs e)
  113. {
  114. OpenFileDialog ofd = new OpenFileDialog();
  115. ofd.InitialDirectory = "D:\\";
  116. if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  117. {
  118. txtSelectFile.Text = ofd.FileName;
  119. }
  120. }
  121. //向服务器端发送文件
  122. private void btnSendFile_Click(object sender, EventArgs e)
  123. {
  124. if (string.IsNullOrEmpty(txtSelectFile.Text))
  125. {
  126. MessageBox.Show("请选择要发送的文件!!!");
  127. }
  128. else
  129. {
  130. // 用文件流打开用户要发送的文件;
  131. using (FileStream fs = new FileStream(txtSelectFile.Text, FileMode.Open))
  132. {
  133. //在发送文件以前先给好友发送这个文件的名字+扩展名,方便后面的保存操作;
  134. string fileName = System.IO.Path.GetFileName(txtSelectFile.Text);
  135. string fileExtension = System.IO.Path.GetExtension(txtSelectFile.Text);
  136. string strMsg = "我给你发送的文件为: " + fileName + "\r\n";
  137. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
  138. byte[] arrSendMsg = new byte[arrMsg.Length + 1];
  139. arrSendMsg[0] = 0; // 用来表示发送的是消息数据
  140. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
  141. sockClient.Send(arrSendMsg); // 发送消息;
  142. byte[] arrFile = new byte[1024 * 1024 * 2];
  143. int length = fs.Read(arrFile, 0, arrFile.Length); // 将文件中的数据读到arrFile数组中;
  144. byte[] arrFileSend = new byte[length + 1];
  145. arrFileSend[0] = 1; // 用来表示发送的是文件数据;
  146. Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length);
  147. // 还有一个 CopyTo的方法,但是在这里不适合; 当然还可以用for循环自己转化;
  148. sockClient.Send(arrFileSend);// 发送数据到服务端;
  149. txtSelectFile.Clear();
  150. }
  151. }
  152. }
  153. }
  154. }
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值