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


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

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

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

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

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

    下面的是界面:

    服务端代码:

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

    客户端代码:

      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 }

    原文出自:https://blog.csdn.net/chwei_cson/article/details/7737766

    对于不可控的事情,保持乐观; 对于可控的事情,保持谨慎
  • 相关阅读:
    算法的时间复杂的分析(推导大O())
    理解快速排序(有图有真相)
    线索二叉树C+Java
    二叉树的顺序存储C+Java
    S3C2440 nand_flash驱动程序
    IMX257实现Ramblock驱动程序编写
    20150410 递归实现八皇后问题
    20150410 递归实现汉诺塔算法
    201504010 IMX257 USB鼠标驱动程序编写
    中缀表达式转后缀表达式
  • 原文地址:https://www.cnblogs.com/baylor2019/p/13801563.html
Copyright © 2020-2023  润新知