C#知识点-13(进程、多线程、使用Socket实现服务器与客户端通信)

avatar
作者
筋斗云
阅读量:3

进程

定义:每一个正在运行的应用程序,都是一个进程 
进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境

            Process[] pros = Process.GetProcesses();//获取电脑中所有正在运行的进程              //通过进程,直接打开文件             //告诉进程,要打开的文件路径,通过PSI对象进行封装             ProcessStartInfo psi = new ProcessStartInfo(@"C:\Users\ThinkPad\Desktop\1.txt");             Process p = new Process();             p.StartInfo = psi;             p.Start();

多线程

        private void button1_Click(object sender, EventArgs e)         {             Test();         }         private void Test()         {             for (int i = 0; i < 100000; i++)             {                 textBox1.Text = i.ToString();             }         }

这段代码在执行完成之前,程序会被卡死(不能操作程序,包括关闭窗口)。因为我们程序在做一些耗时操作的时候,如果主线程去执行某段代码,就没有其余的“精力”去完成其他的操作了。
这时候,我们就需要用到多线程,再新建一个线程来完成耗时操作

        private void button1_Click(object sender, EventArgs e)         {             Thread th = new Thread(Test);             th.Start();         }         private void Test(object str)//如果线程执行的方法,需要参数,我们要求参数的类型必须是object类型         {             for (int i = 0; i < 100000; i++)             {                 textBox1.Text = i.ToString();             }         }

但是使用多线程,也会有很多需要注意的地方。这段代码执行时会提示异常,显示“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”
因为创建textBox1的是主线程,而你创建了一个新的线程th,th调用Test方法会访问主线程创建的控件,这个操作默认是不允许的,我们不允许跨线程的访问,因为这是不安全的。

而如果强制要跨线程访问的话,使用下面这段代码在主窗体加载的时候

        private void Form1_Load(object sender, EventArgs e)         {             //取消跨线程访问的检查             Control.CheckForIllegalCrossThreadCalls = false;         }

但是这样还是有问题,当你运行程序的时候,点击按钮开始计数。如果你在计数途中点击叉号关闭程序,程序还是再运行。这是因为你关闭了主线程(主窗体),但是另一个线程th还在执行。
这时候我们把th这个线程由前台线程转换为后台线程。
前台线程:只有所有的前台线程关闭,程序才关闭
后台线程:只要所有的前台线程结束,后台线程自动结束

        private void button1_Click(object sender, EventArgs e)         {             Thread th = new Thread(Test);             th.IsBackground = true;//将th线程设置为后台线程             th.Start();         }

这时候,没有完成计数时关闭程序后会显示异常“创建窗口句柄时出错。”这是因为关闭程序的时候,主窗体这个前台线程关闭了,程序会调用Dispose这个方法进行线程的资源释放。但是Test方法可能还没执行完,还在使用主线程提供的textBox1这个空间资源,这时候Test方法发现找不到这个资源了,程序就会抛异常。
按理说不是已经把th变成后台线程了吗,不是只要所有的前台线程结束,后台线程就结束吗?为什么还是会抛这个异常。原因是我们的cpu不一定能及时的解决处理线程的操作,因为线程是告诉cpu,“我这个线程准备好了,随时可以操作”,但是具体啥时候操作,程序员说了不算,还要看cpu的“心情”。
我们想让程序不抛这个异常,只能是在关闭窗口的时候,强制关闭后台线程

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)         {             th.Abort();//程序关闭时,强制后台线程关闭         }

使用Socket实现服务器与客户端之间的通信

服务器:
服务器样式截图:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms;  namespace Server {     public partial class Form1 : Form     {         public Form1()         {             InitializeComponent();         }          private void btnStart_Click(object sender, EventArgs e)         {             //1、创建一个监听连接的Socket对象socketWatch,使用IPv4,流式传输,Tcp协议             Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);             //2、创建一个ip地址             IPAddress ip = IPAddress.Parse(txtServer.Text);             //2.1、创建一个端口号             IPEndPoint point = new IPEndPoint(ip,int.Parse(txtPort.Text));             //3、将端口号绑定到socketWatch             socketWatch.Bind(point);             //4、设置监听队列(同一时刻最多有几台设备同时连接)             socketWatch.Listen(10);             ShowMsg("正在等待客户端的连接");             //5、创建一个新线程th,创建线程用于使用新创建的Socket             Thread th = new Thread(MyAccept);             //6、设置th为后台线程             th.IsBackground = true;             //7、开启线程th             th.Start(socketWatch);         }          //客户端的IP地址&端口号,服务器与客户端通讯的Socket         Dictionary<string,Socket> dicSocket = new Dictionary<string,Socket>();         /// <summary>         /// 实现客户端与服务器的通讯         /// </summary>         /// <param name="o"></param>         void MyAccept(object o)         {             //不停的接收客户端的连接             while (true)             {                 //o墙砖为Socket                 Socket socketWatch = o as Socket;                 //为新建连接创建新的与之通信的Socket                 Socket socketTX = socketWatch.Accept();                 //把客户端的IP地址&端口号和与客户端通信的Socket存储到键值对集合中                 dicSocket.Add(socketTX.RemoteEndPoint.ToString(), socketTX);                 //把客户端的ip地址和端口号,存储到下拉框中                 cboUsers.Items.Add(socketTX.RemoteEndPoint.ToString());                 //展示连接的ip地址和端口号                 ShowMsg(socketTX.RemoteEndPoint.ToString() + "连接成功");                 Thread th = new Thread(RecData);                 th.IsBackground = true;                 th.Start(socketTX);             }         }         /// <summary>         /// 不停的接收客户端的消息         /// </summary>         /// <param name="o"></param>         void RecData(object o)         {             Socket socketTX = o as Socket;             while(true)             {                 //创建缓存区                 byte[] buffer = new byte[1024 * 1024 * 5];                 //r表示实际接受到的字节数                 int r = socketTX.Receive(buffer);                 //将接收到的字节数组使用系统默认编码格式转换为字符串                 string msg = Encoding.Default.GetString(buffer, 0, r);                 //展示接收到的信息                 ShowMsg(socketTX.RemoteEndPoint.ToString() + ":" + msg);             }         }         /// <summary>         /// 在文本框中展示信息         /// </summary>         /// <param name="msg"></param>         public void ShowMsg(string msg)         {             txtLog.AppendText(msg + "\r\n");         }          private void Form1_Load(object sender, EventArgs e)         {             Control.CheckForIllegalCrossThreadCalls = false;         }          //服务器给客户端发消息         private void btnSend_Click(object sender, EventArgs e)         {             string msg = txtMsg.Text.Trim();             byte[] buffer = Encoding.Default.GetBytes(msg);             //制作自己的协议 0:文字 1:文件  2:震动             List<byte> listByte = new List<byte>();             listByte.Add(0);             listByte.AddRange(buffer);             //以字节形式发送个客户端的数据,第一个字节是0代表发的是文字             buffer = listByte.ToArray();             //获取服务器选择的客户端的ip地址             string ip = cboUsers.SelectedItem.ToString();             //拿着ip去找对应的socket,然后发送             dicSocket[ip].Send(buffer);         }          //发送震动         private void btnZD_Click(object sender, EventArgs e)         {             byte[] buffer = new byte[1];             buffer[0] = 2;             string ip = cboUsers.SelectedItem.ToString();             dicSocket[ip].Send(buffer);         }          //选择文件         private void btnSelect_Click(object sender, EventArgs e)         {             //创建打开文件对话框             OpenFileDialog ofd = new OpenFileDialog();             ofd.Title = "请选择要发送的文件";             ofd.Filter = "文本文件|*.txt|多媒体文件|*.wmv|所有文件|*.*";             //初始化路径             ofd.InitialDirectory = "E:\\123";             //设置不允许多选             ofd.Multiselect = false;             ofd.ShowDialog();             //获取用户选择文件的全路径             string path = ofd.FileName;             //放到窗体展示出来             txtPath.Text = path;          }          //点击发送文件         private void btnSendFile_Click(object sender, EventArgs e)         {             //获取要发送文件的路径             string path = txtPath.Text.Trim();             using (FileStream fsRead = new FileStream(path,FileMode.Open,FileAccess.Read))             {                 try                 {                     byte[] buffer = new byte[1024 * 1024 * 5];                     int r = fsRead.Read(buffer, 0, buffer.Length);                     List<byte> list = new List<byte>();                     list.Add(1);                     list.AddRange(buffer);                     buffer = list.ToArray();                     //调用跟客户端通信的socket,发送字节数据                     string ip = cboUsers.SelectedItem.ToString();                     dicSocket[ip].Send(buffer, 0, r + 1, SocketFlags.None);                 }                 catch (Exception ex)                 {                     MessageBox.Show(ex.Message);                 }                              }         }     } } 

客户端
客户端样式截图:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Media; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms;  namespace Client {     public partial class Form1 : Form     {         public Form1()         {             InitializeComponent();         }         Socket socket;         private void btnStart_Click(object sender, EventArgs e)         {             socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);             IPAddress ip = IPAddress.Parse(txtServer.Text);             IPEndPoint point = new IPEndPoint(ip,int.Parse(txtPort.Text));             socket.Connect(point);             ShowMsg("连接成功");              //不停的接收服务器发送过来的消息             Thread th = new Thread(RecServerData);             th.IsBackground = true;             th.Start();         }          //不停地接收服务器发送过来的消息         void RecServerData()         {             while (true)             {                 //连接成功后,接收服务器发送过来的消息                 byte[] buffer = new byte[1024 * 1024 * 5];                 int r = socket.Receive(buffer);                 byte b = buffer[0];                 //对面发送过来的是文字                 if (b==0)                 {                     string msg = Encoding.Default.GetString(buffer,1,r-1);                     ShowMsg(msg);                 }                 else if (b==2)//对面发送过来的是震动                 {                     ZhenDong();                     SoundPlayer sp = new SoundPlayer();                     sp.Play();                 }                 else if (b == 1)//对面发送过来的是文件                 {                     //1、弹出来一个保存文件的对话框                     SaveFileDialog sfd = new SaveFileDialog();                     sfd.InitialDirectory = @"E:\123";                     sfd.Title = "请选择要保存的文件路径";                     sfd.Filter = "文本文件|*.txt|媒体文件|*.wmv|所有文件|*.*";                     sfd.ShowDialog(this);//展示保存对话框                     //2、用户在对话框中选择要保存文件的路径                     string savePath = sfd.FileName;                     //3、FileStream把数据写入到指定的路径下                     using (FileStream fsWrite = new FileStream(savePath, FileMode.Create, FileAccess.Write))                     {                         fsWrite.Write(buffer, 1, r - 1);                         MessageBox.Show("保存成功!!!!");                     }                 }             }         }          //窗体震动         void ZhenDong()         {             for (int i = 0; i < 1000; i++)             {                 this.Location = new Point(300, 300);                 this.Location = new Point(320, 320);             }         }         void ShowMsg(string msg)         {             txtLog.AppendText(msg+"\r\n");         }         //客户端给服务器发送消息         private void btnSend_Click(object sender, EventArgs e)         {             string msg = txtMsg.Text.Trim();             byte[] buffer = Encoding.Default.GetBytes(msg);             socket.Send(buffer);         }          private void Form1_Load(object sender, EventArgs e)         {             Control.CheckForIllegalCrossThreadCalls = false;         }     } } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!