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


客户端代码:

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