进程
定义:每一个正在运行的应用程序,都是一个进程
进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境
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、将端口号绑定到socketWatchsocketWatch.Bind(point);//4、设置监听队列(同一时刻最多有几台设备同时连接)socketWatch.Listen(10);ShowMsg("正在等待客户端的连接");//5、创建一个新线程th,创建线程用于使用新创建的SocketThread th = new Thread(MyAccept);//6、设置th为后台线程th.IsBackground = true;//7、开启线程thth.Start(socketWatch);}//客户端的IP地址&端口号,服务器与客户端通讯的SocketDictionary<string,Socket> dicSocket = new Dictionary<string,Socket>();/// <summary>/// 实现客户端与服务器的通讯/// </summary>/// <param name="o"></param>void MyAccept(object o){//不停的接收客户端的连接while (true){//o墙砖为SocketSocket socketWatch = o as Socket;//为新建连接创建新的与之通信的SocketSocket 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;}}
}