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

C#菜鸟做这个东东竟然花了快三天的时间了,真是菜,菜,菜~~~

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

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

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

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

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

下面的是界面:

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

服务端代码:

[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 的对象调用的是本类的其他窗口了,当然不弹出“另存为”窗口。】
  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字节数组;

[csharp] view plaincopyprint?

[csharp] view plaincopyprint?

  1. <span style="font-size:14px;">  byte[] arrSendMsg = new byte[arrMsg.Length + 1]; // 上次写的时候把这一段给弄掉了,实在是抱歉哈~ 用来标识发送是数据而不是文件,如果没有这一段的客户端就接收不到消息了~~~
  2. arrSendMsg[0] = 0; // 表示发送的是消息数据
  3. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);</span>

[csharp] view plaincopyprint?

  1. foreach (Socket s in dict.Values)
  2. {
  3. s.Send(arrMsg);
  4. }
  5. ShowMsg(strMsg);
  6. txtMsgSend.Clear();
  7. ShowMsg(" 群发完毕~~~");
  8. }
  9. // 选择要发送的文件
  10. private void btnSelectFile_Click_1(object sender, EventArgs e)
  11. {
  12. OpenFileDialog ofd = new OpenFileDialog();
  13. ofd.InitialDirectory = "D:\\";
  14. if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  15. {
  16. txtSelectFile.Text = ofd.FileName;
  17. }
  18. }
  19. // 文件的发送
  20. private void btnSendFile_Click_1(object sender, EventArgs e)
  21. {
  22. if (string.IsNullOrEmpty(txtSelectFile.Text))
  23. {
  24. MessageBox.Show("请选择你要发送的文件!!!");
  25. }
  26. else
  27. {
  28. // 用文件流打开用户要发送的文件;
  29. using (FileStream fs = new FileStream(txtSelectFile.Text, FileMode.Open))
  30. {
  31. string fileName=System.IO.Path.GetFileName(txtSelectFile.Text);
  32. string fileExtension=System.IO.Path.GetExtension(txtSelectFile.Text);
  33. string strMsg = "我给你发送的文件为: "+fileName+fileExtension+"\r\n";
  34. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 将要发送的字符串转换成Utf-8字节数组;
  35. byte[] arrSendMsg = new byte[arrMsg.Length + 1];
  36. arrSendMsg[0] = 0; // 表示发送的是消息数据
  37. Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
  38. bool fff = true;
  39. string strKey = "";
  40. strKey = lbOnline.Text.Trim();
  41. if (string.IsNullOrEmpty(strKey))   // 判断是不是选择了发送的对象;
  42. {
  43. MessageBox.Show("请选择你要发送的好友!!!");
  44. }
  45. else
  46. {
  47. dict[strKey].Send(arrSendMsg);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
  48. byte[] arrFile = new byte[1024 * 1024 * 2];
  49. int length = fs.Read(arrFile, 0, arrFile.Length);  // 将文件中的数据读到arrFile数组中;
  50. byte[] arrFileSend = new byte[length + 1];
  51. arrFileSend[0] = 1; // 用来表示发送的是文件数据;
  52. Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length);
  53. // 还有一个 CopyTo的方法,但是在这里不适合; 当然还可以用for循环自己转化;
  54. //  sockClient.Send(arrFileSend);// 发送数据到服务端;
  55. dict[strKey].Send(arrFileSend);// 解决了 sokConnection是局部变量,不能再本函数中引用的问题;
  56. txtSelectFile.Clear();
  57. }
  58. }
  59. }
  60. txtSelectFile.Clear();
  61. }
  62. }
  63. }

客户端代码:

[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. }  
      1. 转自http://blog.csdn.net/chwei_cson/article/details/7737766
时间: 2024-07-29 09:34:00

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

TCP学习之五:客户端、服务端异步传输字符串

参考学习张子阳大神的博客:http://www.cnblogs.com/JimmyZhang/category/101698.html 消息发送接口: 消息接收接口: 客户端: 服务端: 消息发送类: 消息接收类:

TCP练习2: 客户端给服务端发送文本,服务端将文本转成大写再返回给客户端

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import org.junit.Test; /* * 客户端给服务端发送文本,服务端将

python3中实现客户端与服务端交互发送文件

在python3中实现客户端与服务端程序交互,从客户端发送文件到服务端 客户端代码:client.py #!/usr/bin/env python #_*_ encoding=utf-8 _*_ import socket,sys,os ip_port = ('127.0.0.1',9999) sk = socket.socket() sk.connect(ip_port) container = {'key':'','data':''} while True:     input_data =

TCP Socket 通讯(客户端与服务端)

/*----------------------------编译环境:VS2015---------------------------------------*/ /*-----------------------------Author:刘收获-------------------------------------------*/ //Sever端口 // ConsoleApplication1.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h"

超级轻量的远程命令行客户端和服务端(含文件传输)

功能:实现基于TCPIP的命令行操作远程计算机,并且支持远程文件传输 平台:windows   (linux同理) 原理:客户端连接上服务端之后,先监听键盘输入,将键盘输入的cmd指令保存并发送给服务端:服务端保存并通过调用命令行执行,获取命令行执行结果发送给客户端.如果是   获取文件命令,则读取文件,并将文件发送给客户端 代码:源码下载链接 // 核心代码 int getfile(const char *path,SOCKET ClientSocket,const char *file) {

linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现

1 TCP简介 tcp是一种基于流的应用层协议,其"可靠的数据传输"实现的原理就是,"拥塞控制"的滑动窗口机制,该机制包含的算法主要有"慢启动","拥塞避免","快速重传". 2 TCP socket建立和epoll监听实现 数据结构设计 linux环境下,应用层TCP消息体定义如下: typedef struct TcpMsg_s { TcpMsgHeader head; void* msg; }TcpM

[Java]命令行模拟TCP客户端与服务端的简单小程序遇到的问题(基础不牢!扎实和亲手实践比什么都重要!)

简单得不能再简单的需求: 简单模拟TCP客户端与服务端的一次连接和通信,客户端发出一个消息,服务端回馈一个消息 自己第一次编写的代码: Client: class TcpClient1 { public static void main(String[] args) throws Exception { Socket s=new Socket("127.0.0.1",10010); OutputStream out=s.getOutputStream(); out.write(&quo

二、网络编程-socket之TCP协议开发客户端和服务端通信

知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人,信丢了你也不知道,tcp传输需要先和服务端建立连接,当客户端与服务器连接时,服务器会给出应答,我俩连上了,而且数据传过来还会进行一个数据包数量验证,不一致会重新发送,还有其他种种验证,总之保证了数据传输安全可靠   这一章主要介绍使用套接字,编写一个tcp协议客户端和服务端.同样要用到上一章节提到小

网络编程 UDP协议 TCP局域网客户端与服务端上传下载电影示例

UDP协议 (了解) 称之为数据包协议. 特点: 1) 不需要建立链接. 2) 不需要知道对方是否收到. 3) 数据不安全 4) 传输速度快 5)能支持并发 6) 不会粘包 7) 无需先启动服务端再启动客户端 优点: - 传输速度快 - 能支持并发 - 不会粘包 缺点: - 数据不安全, 容易丢失 应用场景: 早期的QQ聊天室. # server端 import socket # socket.SOCK_DGRAM ---> UPD协议 server = socket.socket(type=s