一用C#编写端口扫描器程序
一、创建新项目
1新建一个项目,命名为scanapp
2放置控件
二、端口扫描器
单线程和多线程的区别
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,
即不同的线程可以执行同样的函数。
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,
也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
多线程的好处:
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,
这样就大大提高了程序的效率。
多线程的不利方面:
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
线程太多会导致控制太复杂,最终可能造成很多Bug
1.单线程
代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace scanapp
{
public partial class Form1 : Form
{
//自定义变量
private int start;
private int end;
private string Addr;
private int port;
private bool OK;
private bool[] done = new bool[65536];
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//显示端口扫描的范围
progressBar1.Minimum = Int32.Parse(textBox2.Text);
progressBar1.Maximum = Int32.Parse(textBox3.Text);
//显示框初始化
listBox1.Items.Clear();
listBox1.Items.Add("端口扫描器 v1.0.");
listBox1.Items.Add("");
PortScan();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void label4_TextChanged(object sender, EventArgs e)
{
label4.Text = textBox2.Text;
}
//将输入的结束地址放到进度条的结束位置
private void label6_TextChanged(object sender, EventArgs e)
{
label6.Text = textBox3.Text;
}
private void label4_Click(object sender, EventArgs e)
{
}
private void label6_Click(object sender, EventArgs e)
{
}
private void PortScan()
{
label4.Text = textBox2.Text;
label6.Text = textBox3.Text;
start = Int32.Parse(textBox2.Text);
end = Int32.Parse(textBox3.Text);
//检查输入范围合法性
if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
{
listBox1.Items.Add("开始扫描... (可能需要请您等待几分钟)");
Addr = listBox1.Text;
for (int i = start; i <= end; i++)
{
port = i;
//使用该端口的扫描线程
//scanThread = new Thread(new ThreadStart(Scan));
//scanThread.Start();
//使线程睡眠
System.Threading.Thread.Sleep(100);
progressBar1.Value = i;
label5.Text = i.ToString();
}
//未完成时情况
while (!OK)
{
OK = true;
for (int i = start; i <= end; i++)
{
if (!done[i])
{
OK = false;
break;
}
}
System.Threading.Thread.Sleep(1000);
}
listBox1.Items.Add("扫描结束!");
}
else
{
MessageBox.Show("输入错误,端口范围为[0-65536]");
}
}
private void Scan()
{
int portnow = port;
//创建线程变量
//Thread Threadnow = scanThread;
done[portnow] = true;
//创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
TcpClient objTCP = null;
//扫描端口,成功则写入信息
try
{
//用TcpClient对象扫描端口
objTCP = new TcpClient(Addr, portnow);
listBox1.Items.Add("端口 " + portnow.ToString() + " 开放!");
}
catch
{
}
}
}
}
运行结果:
使用单线程的话,程序执行到一半就卡死了,不能移动不能关闭,只能强制终止程序
2.多线程
代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace scanapp
{
public partial class Form1 : Form
{
//自定义变量
private int start;
private int end;
private string Addr;
private int port;
private Thread scanThread;
private bool OK;
private bool[] done = new bool[65536];
public Form1()
{
InitializeComponent();
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//设置该属性 为false
}
private void button1_Click(object sender, EventArgs e)
{
label4_TextChanged(sender, e);
label6_TextChanged(sender, e);
label4.Visible = true;
label5.Visible = true;
label6.Visible = true;
Thread process = new Thread(new ThreadStart(PortScan));
process.Start();
//显示端口扫描的范围
progressBar1.Minimum = Int32.Parse(textBox2.Text);
progressBar1.Maximum = Int32.Parse(textBox3.Text);
//显示框初始化
listBox1.Items.Clear();
listBox1.Items.Add("端口扫描器 v1.0.");
listBox1.Items.Add("");
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void label4_TextChanged(object sender, EventArgs e)
{
label4.Text = textBox2.Text;
}
//将输入的结束地址放到进度条的结束位置
private void label6_TextChanged(object sender, EventArgs e)
{
label6.Text = textBox3.Text;
}
private void label4_Click(object sender, EventArgs e)
{
}
private void label6_Click(object sender, EventArgs e)
{
}
private void PortScan()
{
start = Int32.Parse(textBox2.Text);
end = Int32.Parse(textBox3.Text);
//检查输入范围合法性
if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
{
listBox1.Items.Add("开始扫描... (可能需要请您等待几分钟)");
Addr = listBox1.Text;
for (int i = start; i <= end; i++)
{
port = i;
//使用该端口的扫描线程
scanThread = new Thread(new ThreadStart(Scan));
scanThread.Start();
//使线程睡眠
System.Threading.Thread.Sleep(100);
progressBar1.Value = i;
label5.Text = i.ToString();
}
//未完成时情况
while (!OK)
{
OK = true;
for (int i = start; i <= end; i++)
{
if (!done[i])
{
OK = false;
break;
}
}
System.Threading.Thread.Sleep(1000);
}
listBox1.Items.Add("扫描结束!");
}
else
{
MessageBox.Show("输入错误,端口范围为[0-65536]");
}
}
private void Scan()
{
int portnow = port;
//创建线程变量
Thread Threadnow = scanThread;
done[portnow] = true;
//创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
TcpClient objTCP = null;
//扫描端口,成功则写入信息
try
{
//用TcpClient对象扫描端口
objTCP = new TcpClient(Addr, portnow);
listBox1.Items.Add("端口 " + portnow.ToString() + " 开放!");
}
catch
{
}
}
}
}
编译运行的时候会出现
线程操作无效
的问题,
在Form1重载中写上一行代码,问题就解决了,详细参考System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnSearch”的线程访问它。”
从程序上来说,只有主线程才能访问控件,其他的子线程不能,所以需要一个委托来跨线程访问
public Form1()
{
InitializeComponent();
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//设置该属性 为false
}
再次运行,运行结果:
二、总结
了解了单线程和多线程的区别
三、参考资料
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnSearch”的线程访问它。”
用C#实现端口扫描器小程序
利用单线程和多线程实现端口扫描器
C# 多线程