进程
定义:每一个正在运行的应用程序,都是一个进程
进程不等于正在运行的应用程序。而是为应用程序的运行构建一个运行环境
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;
}
}
}