zoukankan      html  css  js  c++  java
  • 基于socket、多线程的客户端服务器端聊天程序

    服务器端:

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



    客户端:

    [csharp] view plaincopyprint?
     
      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.   
      8.   
      9. namespace MyChatRoomClient  
      10. {  
      11.     public partial class FChatClient : Form  
      12.     {  
      13.         public FChatClient()  
      14.         {  
      15.             InitializeComponent();  
      16.             //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查  
      17.             //关闭跨线程检查  
      18.             TextBox.CheckForIllegalCrossThreadCalls = false;  
      19.         }  
      20.   
      21.   
      22.         IPAddress address = null;  
      23.         IPEndPoint endpoint = null;  
      24.         Socket socketClient = null//客户端套接字  
      25.   
      26.   
      27.   
      28.   
      29.         Thread threadClient = null;//客户端负责接受服务端发来的消息的线程  
      30.         //客户端发送请求到服务器  
      31.         private void btnConnect_Click(object sender, EventArgs e)  
      32.         {  
      33.             address = IPAddress.Parse(txtIP.Text.Trim());  
      34.             endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));  
      35.             socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
      36.             try  
      37.             {  
      38.                 socketClient.Connect(endpoint);  
      39.                 //创建线程,监听服务器端发来的消息  
      40.                 threadClient = new Thread(RecMsg);  
      41.                 threadClient.IsBackground = true;  
      42.                 threadClient.Start();  
      43.                   
      44.             }  
      45.             catch (Exception ee)  
      46.             {  
      47.                 MessageBox.Show(ee.ToString());  
      48.             }  
      49.         }  
      50.         /// <summary>  
      51.         /// 监听服务器端发来的消息  
      52.         /// </summary>  
      53.         void RecMsg()  
      54.         {  
      55.             while (true)  
      56.             {  
      57.                 //定义一个2M的接受数据的缓存区  
      58.                 byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间  
      59.                 //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度  
      60.                 int length = socketClient.Receive(arrMsgRec);  
      61.                 //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符  
      62.                 string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);  
      63.                 ShowMsg(strMsgRec);  
      64.             }  
      65.         }  
      66.         #region 在窗体文本框中显示消息-void ShowMsg(string msg)  
      67.         /// <summary>  
      68.         /// 在窗体文本框中显示消息  
      69.         /// </summary>  
      70.         /// <param name="msg">消息</param>  
      71.         void ShowMsg(string msg)  
      72.         {  
      73.             txtMsg.AppendText(msg + " ");  
      74.         }   
      75.         #endregion  
      76.  
      77.  
      78.         #region 选择要发送的文件-btnChooseFile_Click  
      79.         //选择要发送的文件  
      80.         private void btnChooseFile_Click(object sender, EventArgs e)  
      81.         {  
      82.             OpenFileDialog ofd = new OpenFileDialog();  
      83.             if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)  
      84.             {  
      85.                 txtFilePath.Text = ofd.FileName;  
      86.             }  
      87.         }  
      88.         #endregion  
      89.   
      90.   
      91.   
      92.   
      93.   
      94.   
      95.         //向服务器发送文本消息  
      96.         private void btnSendMsg_Click(object sender, EventArgs e)  
      97.         {  
      98.             string strMsg = txtMsgSend.Text.Trim();  
      99.             byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);  
      100.             byte[] arrMsgSend = new byte[arrMsg.Length + 1];  
      101.             arrMsgSend[0] = 0;//设置标识位,0代表是文字  
      102.             Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);  
      103.             socketClient.Send(arrMsgSend);  
      104.             ShowMsg("我发送了:" + strMsg);  
      105.         }  
      106.   
      107.   
      108.         //向服务端发送文件  
      109.         private void btnSendFile_Click(object sender, EventArgs e)  
      110.         {  
      111.             //用文件流打开用户选择的文件  
      112.             using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))  
      113.             {  
      114.                 byte[] arrFile = new byte[1024*1024*2];//定义一个2M缓存区  
      115.                 //将文件数据读到数组arrFile中,并获得读取的真是数据长度  
      116.                 int length = fs.Read(arrFile,0,arrFile.Length);  
      117.                 byte[] arrFileSend = new byte[length + 1];  
      118.                 arrFileSend[0] = 1;//代表发送的是文件数据  
      119.                 //for (int i = 0; i < length; i++)  
      120.                 //{  
      121.                 //    arrFileSend[i + 1] = arrFile[i];  
      122.                 //}  
      123.                 //数据块的拷贝,将arrFile从第0个开始拷贝,拷贝到arrFileSend,从第一个开始存放  
      124.                 Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);  
      125.                 //arrFile.CopyTo(arrFileSend,length);只能从0开始拷贝  
      126.                 //发送了包含了标识位的新数据到服务端  
      127.                 socketClient.Send(arrFileSend);  
      128.             }  
      129.         }  
      130.           
      131.     }  
      132. }  
    感谢来访,共同学习!
  • 相关阅读:
    Appium+python自动化20-查看iOS上app元素属性【转载】
    Appium+python自动化19-iOS模拟器(iOS Simulator)安装自家APP【转载】
    Appium+python自动化18-brew、carthage和appium-doctor【转载】
    Appium+python自动化17-启动iOS模拟器APP源码案例【转载】
    Appium+python自动化16-appium1.6在mac上环境搭建启动ios模拟器上Safari浏览器【转载】
    Appium+python自动化15-在Mac上环境搭建【转载】
    Centos-清屏命令-clear
    Centos-切换用户身份-su
    Centos-修改密码-passwd
    Centos-显示或修改系统时间与日期-date
  • 原文地址:https://www.cnblogs.com/dingxiaowei/p/3246417.html
Copyright © 2011-2022 走看看