进程:我们每一个应用程序都是一个进程,而进程又是由多个线程组成的。
//获取计算机所有进程
Process[] pros = Process.GetProcesses();
foreach(var item in pros)
{
Console.WriteLine(item);
}
//获取当前进程
Console.WriteLine(Process.GetCurrentProcess().ToString());
//启动画图进程
Process.Start("mspaint");
线程分为前台线程和后台线程。
前台线程:只有所有的前台线程都关闭,才能完成程序关闭。
后台线程:只要所有的前台线程结束,后台线程就会自动结束。
Winform窗体示例如下:
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_窗体
{
public partial class Form1 : Form
{
Thread th;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//取消对跨线程访问的检查
Control.CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
//开启一个新的线程帮助我们执行Test方法
th = new Thread(Test);
//设置成后台线程
th.IsBackground = true;
//启动线程(注意:线程一旦被关闭,就不能再被打开了)
th.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//当关闭窗体的时候,同时关闭我的后台线程
if (th != null)
{
//终止线程
th.Abort();
}
}
void Test()
{
for(int i=0;i<10000;i++)
{
textBox1.Text = i.ToString();
}
}
}
}
为什么要用多线程?
1. 让计算机同时做多件事情,节约时间;
2. 后台运行程序,提高程序运行效率,也不会使主界面出现无响应的情况。
注意:Thread.Sleep(3000);可以让当前线程停止3秒后 再运行。
多线程执行带参数的方法:
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_多线程执行带参数的方法
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//线程执行的方法 如果有参数,那么这个参数必须是object类型
Thread th = new Thread(Test);
th.IsBackground = true;
th.Start("123");
}
public void Test(object str)
{
string s = (string)str;
MessageBox.Show(s);
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}
使用多线程来实现一个摇奖机
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_摇奖机
{
public partial class Form1 : Form
{
Thread th;
bool b = false;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (b == false)
{
th = new Thread(GameStart);
th.IsBackground = true;
th.Start();
b = true;
button1.Text = "暂停";
}
else
{
b = false;
button1.Text = "开始";
}
}
public void GameStart()
{
Random r = new Random();
while(b)
{
try
{
label1.Text = r.Next(0, 10).ToString();
label2.Text = r.Next(0, 10).ToString();
label3.Text = r.Next(0, 10).ToString();
}
catch
{
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(th!=null)
{
th.Abort();
}
}
}
}
运行结果为:
Socket网络编程
人通过【电话】通信;
程序通过【Socket】通信;
故而,Socket(套接字)就是程序间的电话机。
我和另一个人打电话,在电话中规定好的、都听得懂的语言:汉语;
电脑和另一台电脑进行联系(通信),在Socket中 规定好的、都听得懂的语言:HTTP协议。
Socket相关概念:
Socket翻译过来的意思是“孔” “插座”。作为进程通信机制,就选取后一种意思,通常也被称作“套接字”,用于描述IP地址和端口号,是一个通信链的句柄(其实就是两个程序互相通信用的)。
在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)。
例如:http协议使用80端口,ftp协议使用21端口,smtp协议使用25端口。我们自己使用端口的时候一般采用50000以后的端口,避免重用端口。
Socket有两种类型:
1. 流式Socket(Stream):是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全但是效率低;
2. 数据报式Socket(Datagram):是一种无连接的Socket,对应于无连接的UDP服务应用,不安全(易丢包,顺序混乱,在接收端要分析重排及要求重发),但效率高。
服务端的Socket(至少需要两个):
1. 一个负责接收客户端连接请求(但不负责与客户端通信);
2. 每成功接收到一个客户端的连接请求,便在服务端产生一个对应的 负责通信的Socket;
(1)在接收到客户端连接时创建;
(2)为每个连接成功的客户端请求 在服务端创建一个对应的Socket(负责和客户端通信)。
客户端的Socket:
1. 必须指定要连接的服务端地址IP和端口号port;
2. 通过创建一个Socket对象来初始化一个到服务端的TCP连接。
Socket相关类和方法
相关类:
IPAddress类:包含了一个IP地址;
IPEndPoint类:包含了一对IP地址和端口号;
方法:
Socket(); //创建一个Socket;
Bind(); //绑定一个本地的IP和端口号(IPEndPoint);
Listen(); //让Socket侦听传入的连接,尝试并指定侦听队列的容量;
Connect(); //初始化 与另一个Socket的连接;
Accept(); //接收连接并返回一个新的socket;
Send(); //输出数据到Socket;
Receive(); //从Socket中读取数据;
Close(); 关闭Socket(销毁连接);
示例一:使用TCP协议实现服务端与n个客户端(n>=1)进行通信(互相收发数据),0-发送普通文本,1-发送文件,2-发送震动;
服务端代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Server
{
public partial class Form1 : Form
{
Socket socketSend;
//将远程连接的客户端的IP地址和Socket存入集合中
Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
public Form1()
{
InitializeComponent();
}
private void btn_Start_Click(object sender, EventArgs e)
{
try
{
//创建一个负责监听的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建IP地址和端口号
IPAddress ip = IPAddress.Any;//IPAddress.Parse(txt_IP.Text);
IPEndPoint endPoint = new IPEndPoint(ip, Convert.ToInt32(txt_Port.Text));
//让负责监听的Socket绑定IP地址和端口号
socketWatch.Bind(endPoint);
ShowMsg("监听成功");
btn_Start.Enabled = false;
//设置监听队列
socketWatch.Listen(10);
Thread th = new Thread(Listen);
th.IsBackground = true;
th.Start(socketWatch);
}
catch
{ }
}
/// <summary>
/// 等待客户端的连接,并创建与之通信用的Socket
/// </summary>
void Listen(object o)
{
Socket socketWatch = o as Socket;
//等待客户端的连接 并创建一个负责通信的Socket
while (true)
{
try
{
//负责跟客户端通信的Socket
socketSend = socketWatch.Accept();
//将远程连接的客户端的IP地址和Socket存入集合中
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//将远程连接的客户端的IP地址和端口号存入下拉框中
comboBox_SelectItem.Items.Add(socketSend.RemoteEndPoint.ToString());
ShowMsg(socketSend.RemoteEndPoint.ToString() + "连接成功");
//开启一个线程:不停地接收客户端发来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start(socketSend);
}
catch
{ }
}
}
/// <summary>
/// 服务端不停地接收客户端发来的消息
/// </summary>
/// <param name="o"></param>
void Receive(object o)
{
Socket socketSend = o 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 + ":" + str);
}
catch
{ }
}
}
void ShowMsg(string str)
{
txt_Log.AppendText(str + "\r\n");
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 服务端给客户端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Send_Click(object sender, EventArgs e)
{
string str = txt_Msg.Text;
byte[] buffer = Encoding.UTF8.GetBytes(str);
List<byte> list = new List<byte>();
list.Add(0);
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
//根据下拉框是否被选择,来确定:是把消息发给单个客户端,还是所有客户端
if (comboBox_SelectItem.SelectedIndex==-1)
{
//向所有客户端发数据
foreach(var item in comboBox_SelectItem.Items)
{
string ip = item.ToString();
dicSocket[ip].Send(newBuffer);
}
}
else
{
//向选择的指定客户端发数据(获得用户在下拉框中选中的IP地址)
string ip = comboBox_SelectItem.SelectedItem.ToString();
dicSocket[ip].Send(newBuffer);
}
}
/// <summary>
/// 选择要发送的文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Select_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.InitialDirectory = @"D:\Program Files";
ofd.Title = "请选择要发送的文件";
ofd.Filter = "所有文件|*.*";
ofd.ShowDialog();
txt_Path.Text = ofd.FileName;
}
/// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_SendFile_Click(object sender, EventArgs e)
{
//获得要发送的文件路径
string path = txt_Path.Text;
using (FileStream fsRead=new FileStream(path,FileMode.Open,FileAccess.Read))
{
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);
byte[] newBuffer = list.ToArray();
dicSocket[comboBox_SelectItem.SelectedItem.ToString()].Send(newBuffer,0,r+1,SocketFlags.None);
}
}
/// <summary>
/// 发送震动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Shake_Click(object sender, EventArgs e)
{
byte[] buffer = new byte[1];
buffer[0] = 2;
dicSocket[comboBox_SelectItem.SelectedItem.ToString()].Send(buffer);
}
}
}
客户端代码:
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Client
{
public partial class Form1 : Form
{
Socket socketSend;
public Form1()
{
InitializeComponent();
}
private void btn_Start_Click(object sender, EventArgs e)
{
try
{
//创建负责通信的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(txt_IP.Text);
IPEndPoint endPoint = new IPEndPoint(ip, Convert.ToInt32(txt_Port.Text));
//获得要连接的远程服务端应用程序的IP地址和端口号
socketSend.Connect(endPoint);
ShowMsg("连接成功");
btn_Start.Enabled = false;
//开启一个新的线程 不停地接收服务端发来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start();
}
catch
{ }
}
/// <summary>
/// 不停地接收服务端发来的消息
/// </summary>
void Receive()
{
while(true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 3];
//实际接收到的有效字节数
int r = socketSend.Receive(buffer);
if (r == 0)
{
break;
}
switch (buffer[0])
{
case 0://0:表示服务端发送来的是文字类信息
string s = Encoding.UTF8.GetString(buffer, 1, r - 1);
ShowMsg(socketSend.RemoteEndPoint + ":" + s);
break;
case 1://1:表示服务端发送来的是文件类信息
SaveFileDialog sfd = new SaveFileDialog();
sfd.InitialDirectory = @"D:\Program Files";
sfd.Title = "请选择要保存的文件";
sfd.Filter = "所有文件|*.*";
sfd.ShowDialog(this);
string path = sfd.FileName;
using (FileStream fsWrite=new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
{
fsWrite.Write(buffer, 1, r - 1);
}
MessageBox.Show("保存成功");
break;
case 2://2:表示服务端发送来的是震动信息
Shake();
break;
}
}
catch
{ }
}
}
/// <summary>
/// 震动
/// </summary>
void Shake()
{
for(int i=0;i<500;i++)
{
this.Location = new Point(200, 200);
this.Location = new Point(280, 280);
}
}
void ShowMsg(string str)
{
txt_Log.AppendText(str + "\r\n");
}
/// <summary>
/// 客户端给服务端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Send_Click(object sender, EventArgs e)
{
string str = txt_Msg.Text.Trim();
byte[] buffer = Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}
运行结果为:
(1)收发文本类数据
(2)收发文件类数据
(3)收发震动数据
源码链接:https://pan.baidu.com/s/1vsPxxwvqWwVPxgxuQHvRnQ
提取码:15sv