C#学习笔记——进程类
一、进程(Process)
- 我们可以把计算机中每一个运行的应用程序当作是一个进程,而一个进程又是由多个线程组成的。
- 通过进程可以查看电脑进程状况,打开一些电脑可执行应用程序(如:计算器、绘图本等)。
- 注意:
在打开一些非可执行文件时,需要注意自己的环境,如果在.NET Core中,需要将ProcessStartInfo中的UseShellExecute设置为true。而在.NET FrameWork中。该属性默认为true。如不打开,系统会出现如下异常。
- UseShellExecute的默认设置
using System;
using System.Diagnostics;
namespace 进程类
{
class Program
{
static void Main(string[] args)
{
Process[] pros = Process.GetProcesses();
foreach (var item in pros)
{
//item.Kill();结束所有进程,猛男建议尝试
Console.WriteLine(item);
}
//通过进程打开一些应用程序
Process.Start("calc");//打开计算器
Process.Start("mspaint");//打开绘图本
//打开浏览器
#region 方法一
//ProcessStartInfo psi = new ProcessStartInfo
//{
// FileName = "http://www.baidu.com",
// //在.NET Core中想要打开网页链接需要将此处设为true,因为浏览器并不是可执行文件
// UseShellExecute = true
//};
//Process.Start(psi);
#endregion
#region 方法二 推荐使用
ProcessStartInfo psi = new ProcessStartInfo("http://www.baidu.com");
psi.UseShellExecute = true;
Process.Start(psi);
#endregion
#region 方法三
//ProcessStartInfo psi = new ProcessStartInfo("msedge", "http://www.baidu.com");
//psi.UseShellExecute = true;
//Process pros = new Process();
//pros.StartInfo = psi;
//pros.Start();
#endregion
//通过一个进程打开指定的文件
ProcessStartInfo psi = new ProcessStartInfo(@"C:\Users\Administrator\Desktop\1.txt");
psi.UseShellExecute = true;
Process pros = new Process();
pros.StartInfo = psi;//StartInfo需要一个ProcessStartInfo的对象
pros.Start();
}
}
}
二、线程(Thread)
1、前台线程和后台线程
- **前台线程:**只有所有的前台线程都关闭才能完成程序关闭
- **后台线程:**只要所有的前台线程结束,后台线程自动结束
2、多线程
-
为什么要用多线程?
1、单线程会带来"假死"现象;
2、让计算机"同时"做多件事件,节约时间;
3、多线程可以让程序"同时"处理多个事情;
4、后台运行程序,提高程序的运行效率,也不会使主界面出现无响应的情况 -
.NET如何实现多线程(线程同步)
1、编写产生线程所要执行的方法;
2、引用System.Threading命名空间
3、实例化Thread类,并传入一个指向线程所要运行方法的委托。(这个时候线程已经产生,但是还没有运行)
4、调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定 -
在.Net下,不允许跨线程的访问
-
当线程被终止(Abort)后,就无法在重新启动(Start)了
-
线程在未执行完时被关闭,可能会导致系统的资源无法被释放并抛出异常,此时可判断新进程是否为null,如果不是,可手动用Abort将线程终止。
-
线程中如何访问其他控件?
C#在线程中禁止访问别的控件,但是,可以在程序被加载时取消系统的跨线程检查;
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
- 线程中如何访问带参数的方法?
1、如果线程执行的方法需要参数,那么要求这个参数必须时object类型;
2、参数在Start中放入。
3、Thread类的一些重要成员
- Start() 启动线程:告诉CPU该线程可以被执行,具体什么时候执行,由CPU决定
- Abort() 终止线程:直至完成之后不能再Start();
- Thread.Sleep():静态方法,可以使当前线程停止一段时间运行;
- Name:线程名
- Thread.CurrentThread:获得当前的线程引用
三、Socket网络编程
- 人通过【电话】可以通信
程序通过【Socket】来通信
套接字就是程序间的电话机 - 人和人直接打电话 ~~~~ 电话 ~~~~ 规定好的语言(如普通话)
电脑和电脑进行联系 ~~~~ Socket ~~~~ 协议(如HTTP、TCP、UDP)
1、Socket相关概念
-
socket:作为进程通信机制,通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄(其实就是两个程序通信用的)
理解:假设客户端想要和服务器的某个应用程序进行联系,就需要知道服务器的IP地址,但服务器中的IP地址都一样,想要与具体的应用程序通信,此时就需要端口号。 -
端口:在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务,每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序);
----例如:http使用80端口、ftp使用21端口、smtp使用25端口(我们一般使用50000以后的端口) -
Socket具有两种类型:
1、流式Socket(STREAM):
----是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但效率低;
2、数据报式Socket(DATAGRAM):
----式一种无连接的Socket,对应于无连接的UDP服务应用,不安全(对视,顺序混乱,在连接端要分析重排及要求重发),但效率高 -
注意:
1、一个Socket一次智能连接一台主机
2、Socket关闭后无法再次使用
3、每个Socket对象只能一台远程主机连接,如果你想连接到多台远程主机,你必须创建多个Socket对象 -
更贴切的理解,请点击我的另外一篇文章.
2、Socket一般应用模式
也可用下面这个更直观的理解
- 服务端的Socket(至少需要两个)
1、一个负责接收客户端连接请求(但不负责与客户端通信)
2、每成功接收到一个客户端的连接,便在服务端产生一个对应的负责通信的Socket
----在接收到客户端连接时创建
----为每个连接层高的哭护短请求在服务端都创建一个对应的Socket(负责和客户端通信) - 客户端的Socket
1、必须指定要连接的服务器地址和端口
2、通过创建一个Socket对象来初始化一个到服务器端的TCP连接
3、Socket通信基本流程图
四、案例
1、摇奖机
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 摇奖机
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool b = false;
private void button1_Click(object sender, EventArgs e)
{
if(b==false)
{
b = true;
button1.Text = "暂停";
Thread th = new Thread(PlayGame);
th.IsBackground = true;
th.Start();
}
else
{
b = false;
button1.Text = "开始";
}
}
private void PlayGame()
{
Random r = new Random();
while (b)
{
label1.Text = r.Next(0, 10).ToString();
label2.Text = r.Next(0, 10).ToString();
label3.Text = r.Next(0, 10).ToString();
}
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}
2、Socket网络编程
1>服务器端
- a、创建一个负责监听的Socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
参数含义:
1、AddressFamily
2、SocketType
3、ProtocolType
- b、创建IP地址和端口号对象
//创建IP地址
IPAddress ip = IPAddress.Any;//IPAddress.Parse(txtServe.Text);
//创建端口号对象
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
- c、让负责监听的Socket绑定IP地址跟端口号
//让负责监听的Socket绑定IP地址跟端口号
socketListen.Bind(point);
- e、设置监听队列:在某一个时间点内能够连入服务端的最大客户端数量
//设置监听队列:在某一个时间点内能够连入服务端的最大客户端数量
socketListen.Listen(10);
- f、 负责监听的Socket 来接收客户端的连接,同时创建跟客户端通信的socket
//负责监听的Socket 来接收客户端的连接 创建跟客户端通信的socket
Socket socketSend = socketListen.Accept();//返回的socket为接收信息的eSocket
- g、接收客户端发来的信息
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);
2>客户端
- a、创建一个负责和客户端通信的Socket
//在客户端创建一个负责跟客户端通信使用的socket
Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- b、获得想要通信的服务器的IP地址和端口号
//获得要连接的服务器的IP地址
IPAddress ip = IPAddress.Parse(tbIP.Text);
//获得要连接服务器的端口号
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(tbCOM.Text));
- c、客户端负责通信的Socket去连接服务端的IP地址跟端口号
socketSend.Connect(point);
- d、客户端给服务器发送信息
//获得客户端想要发送的信息
string str = txtMsg.Text.Trim();
byte[] buffer = Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
- e、接收服务器发送的信息
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);
3>自定义"协议"
- 当客户端或者服务器想要给对方发送文档时,该文档可能是文件,也可使是字符串文本,因此,可以通过自定义的协议来完成区分文档类型;
1、把要传递的字节数组签名都加上一个字节作为标识(比如:0:表示文字,1:表示文件)
2、经过以上操作,传输的文字流就变成了:0 + 文字(字节数组表示);
传输的文件就变成了:1+文件的二进制信息;
3、接收信息时,就可以通过第一个字节来区分文档,同时从第二个字节开始保存或输出文档; - 除了区分这种大的文档形式外,还可以通过自定义协议区分文件的类型(比如在文件的字节数组中在添加一个标识,作为文件的分类标识)
4>代码
a、服务器
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 btnListen_Click(object sender, EventArgs e)
{
try
{
//创建一个负责监听的Socket
Socket socketListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建IP地址
IPAddress ip = IPAddress.Any;//IPAddress.Parse(txtServe.Text);
//创建端口号对象
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//让负责监听的Socket绑定IP地址跟端口号
socketListen.Bind(point);
ShowMsg("监听成功");
//设置监听队列:在某一个时间点内能够连入服务端的最大客户端数量
socketListen.Listen(10);
//开启新的线程来执行Listen方法
Thread th1 = new Thread(Listen);
th1.IsBackground = true;
th1.Start(socketListen);
}
catch
{
}
}
void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n");
}
Socket socketSend;
//将远程连接的客户端的IP地址存入键值对集合中
Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
/// <summary>
/// 监听客户端请求 并创建与之通信的Socket
/// </summary>
/// <param name="obj">负责监听的Socket,因为要放入线程,所以参数必须为object类型</param>
void Listen(object obj)
{
Socket socketListen = obj as Socket;//里氏转换
while (true)
{
try
{
//负责监听的Socket 来接收客户端的连接 创建跟客户端通信的socket
socketSend = socketListen.Accept();//返回的socket为接收信息的eSocket
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//将远程连接的客户端的IP地址和端口号存储在下拉框中
cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());
ShowMsg(socketSend.RemoteEndPoint.ToString() + "连接成功");//获得远程通信的客户端的IP地址和端口号
//开启新线程,不停的接收客户端发来的信息
Thread th2 = new Thread(Recept);
th2.IsBackground = true;
th2.Start(socketSend);
}
catch
{
}
}
}
/// <summary>
/// 不停的接收客户端发来的信息
/// </summary>
/// <param name="obj"></param>
private void Recept(object obj)
{
Socket socketSend = obj as Socket;
while (true)
{
try
{
//服务端开始接收客户端发来的数据
byte[] buffer = new byte[1024 * 1024 * 2];
//服务端实际接收到的有效字节数
int r = socketSend.Receive(buffer);
//客户端下线后跳出
if (r == 0)
break;
//将字节数组转为字符串
string str = Encoding.UTF8.GetString(buffer, 0, r);
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
}
catch
{
}
}
}
/// <summary>
/// 服务器给客户端发送文本消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
string str = txtMsg.Text;
byte[] buffer = Encoding.UTF8.GetBytes(str);
List<byte> list = new List<byte>();
list.Add(0);
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
string ip = cboUsers.SelectedItem.ToString();
dicSocket[ip].Send(newBuffer);
}
/// <summary>
/// 选择要发送的文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "请选择您要发送的文件";
ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
ofd.Filter = "文本文件|*.txt|音乐文件|*.wav|所有文件|*.*";
ofd.ShowDialog();
txtPath.Text = ofd.FileName;
}
/// <summary>
/// 服务器给客户端发送选中的文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
//获得要发送文件的路径
string path = txtPath.Text;
using(FileStream fsRead = new FileStream(path,FileMode.Open,FileAccess.Read))
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r = fsRead.Read(buffer, 0, buffer.Length);
List<byte> list = new List<byte>();
list.Add(1);
list.AddRange(buffer);
byte[] newBuffer = list.ToArray();
dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer,0,r+1,SocketFlags.None);
}
}
/// <summary>
/// 发送振动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGo_Click(object sender, EventArgs e)
{
byte[] buffer = new byte[1];
buffer[0] = 2;
dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer);
}
}
}
b、客户端
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 Client
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Socket socketSend;
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
//在客户端创建一个负责跟客户端通信使用的socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获得要连接的服务器的IP地址
IPAddress ip = IPAddress.Parse(tbIP.Text);
//获得要连接服务器的端口号
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(tbCOM.Text));
//客户端负责通信的Socket去连接服务端的IP地址跟端口号
socketSend.Connect(point);
ShowMsg("连接成功");
//开启一个新的线程不停的接收服务器发来的信息
Thread th = new Thread(Recept);
th.IsBackground = true;
th.Start();
}
catch
{
}
}
void ShowMsg(string str)
{
txtLog.AppendText(str + "\r\n");
}
/// <summary>
/// 客户端给服务器发送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSent_Click(object sender, EventArgs e)
{
//获得客户端想要发送的信息
string str = txtMsg.Text.Trim();
byte[] buffer = Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
}
void Recept()
{
while (true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);
if (r == 0)
break;
//如果服务器发送的是文本
if (buffer[0] == 0)
{
string str = Encoding.UTF8.GetString(buffer, 1, r-1);
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
}
//如果客户端发送的是文件
else if(buffer[0]==1)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "请选择你要保存的路径";
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
sfd.Filter = "文本文件|*.txt|音频文件|*.wav|所有文件|*.*";
sfd.ShowDialog();
string path = sfd.FileName;
using(FileStream fsWrite =new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
{
fsWrite.Write(buffer, 1, r - 1);
}
ShowMsg("文件保存成功");
}
else if(buffer[0]==2)
{
ZD();
}
}
catch
{
}
}
}
/// <summary>
/// 振动方法
/// </summary>
void ZD()
{
for (int i = 0; i < 200; i++)
{
this.Location = new Point(200, 200);
this.Location = new Point(280, 280);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}