1.vs2017建立工程文件
2.点击进入 From.cs[设计],可将界面设计如下:
1.编写代码
摆放完毕后,在窗口设计界面内,双击 button 按钮,可以转到代码编辑区。 以下是我的代码,也有部分注释。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
namespace PortScan
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//主机地址
private string hostAddress;
//起始端口
private int start;
//终止端口
private int end;
//端口号
private int port;
//定义线程对象
private Thread scanThread;
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void textBox4_TextChanged(object sender, EventArgs e)
{
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
private void label4_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//初始化
textBox4.Clear();
label5.Text = "0%";
//获取ip地址和始末端口号
hostAddress = textBox1.Text;
start = Int32.Parse(textBox2.Text);
end = Int32.Parse(textBox3.Text);
if (decideAddress())
{
//让输入的textbox只读,无法改变
textBox1.ReadOnly = true;
textBox2.ReadOnly = true;
textBox3.ReadOnly = true;
//设置进度条的范围
progressBar1.Minimum = start;
progressBar1.Maximum = end;
//显示框显示
textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
//调用端口扫描函数
PortScan();
}
else
{
//若端口号不合理,弹窗报错
MessageBox.Show("输入错误,端口范围为[0-65536]!");
}
}
catch
{
//若输入的端口号为非整型,则弹窗报错
MessageBox.Show("输入错误,端口范围为[0-65536]!");
}
}
private bool decideAddress()
{
//判断端口号是否合理
if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
return true;
else
return false;
}
private void PortScan()
{
double x;
string xian;
//显示扫描状态
textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
//循环抛出线程扫描端口
for (int i = start; i <= end; i++)
{
x = (double)(i - start + 1) / (end - start + 1);
xian = x.ToString("0%");
port = i;
//调用端口i的扫描操作
Scan();
//进度条值改变
label5.Text = xian;
label5.Refresh();
progressBar1.Value = i;
}
textBox3.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
//输入框textbox只读属性取消
textBox1.ReadOnly = false;
textBox2.ReadOnly = false;
textBox3.ReadOnly = false;
}
private void Scan()
{
int portnow = port;
//创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
TcpClient objTCP = null;
try
{
//用于TcpClient对象扫描端口
objTCP = new TcpClient(hostAddress, portnow);
//扫描到则显示到显示框
textBox3.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
}
catch
{
//未扫描到,则会抛出错误
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox4_TextChanged_1(object sender, EventArgs e)
{
}
private void textBox4_TextChanged_2(object sender, EventArgs e)
{
}
}
}
2.执行结果
这里说明一下:127.0.0.1这个 IP 地址代指自己的主机,不能用自己主机真实的 IP 地址。
可以看到扫描的速度是比较慢的。
将单线程的代码稍微修改一下,加入多线程
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
namespace PortScan
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
//主机地址
private string hostAddress;
//起始端口
private int start;
//终止端口
private int end;
//端口号
private int port;
//定义线程对象
private Thread scanThread;
private bool[] done = new bool[65526];
private bool OK;
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void textBox4_TextChanged(object sender, EventArgs e)
{
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
private void label4_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//初始化
textBox4.Clear();
label5.Text = "0%";
//获取ip地址和始末端口号
hostAddress = textBox1.Text;
start = Int32.Parse(textBox2.Text);
end = Int32.Parse(textBox3.Text);
if (decideAddress())
{
textBox1.ReadOnly = true;
textBox2.ReadOnly = true;
textBox3.ReadOnly = true;
//创建线程,并创建ThreadStart委托对象
Thread process = new Thread(new ThreadStart(PortScan));
process.Start();
//设置进度条的范围
progressBar1.Minimum = start;
progressBar1.Maximum = end;
//显示框显示
textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
}
else
{
//若端口号不合理,弹窗报错
MessageBox.Show("输入错误,端口范围为[0-65536]!");
}
}
catch
{
//若输入的端口号为非整型,则弹窗报错
MessageBox.Show("输入错误,端口范围为[0-65536]!");
}
}
private bool decideAddress()
{
//判断端口号是否合理
if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
return true;
else
return false;
}
private void PortScan()
{
double x;
string xian;
//显示扫描状态
textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
//循环抛出线程扫描端口
for (int i = start; i <= end; i++)
{
x = (double)(i - start + 1) / (end - start + 1);
xian = x.ToString("0%");
port = i;
//使用该端口的扫描线程
scanThread = new Thread(new ThreadStart(Scan));
scanThread.Start();
//使线程睡眠
System.Threading.Thread.Sleep(100);
//进度条值改变
label5.Text = xian;
progressBar1.Value = i;
}
while (!OK)
{
OK = true;
for (int i = start; i <= end; i++)
{
if (!done[i])
{
OK = false;
break;
}
}
System.Threading.Thread.Sleep(1000);
}
textBox4.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
textBox1.ReadOnly = false;
textBox2.ReadOnly = false;
textBox3.ReadOnly = false;
}
private void Scan()
{
int portnow = port;
//创建线程变量
Thread Threadnow = scanThread;
//扫描端口,成功则写入信息
done[portnow] = true;
//创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
TcpClient objTCP = null;
try
{
//用于TcpClient对象扫描端口
objTCP = new TcpClient(hostAddress, portnow);
//扫描到则显示到显示框
textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
}
catch
{
//未扫描到,则会抛出错误
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void textBox4_TextChanged_1(object sender, EventArgs e)
{
}
private void textBox4_TextChanged_2(object sender, EventArgs e)
{
}
}
}
结果如图,在扫描的时候比单线程的速度快了很多倍;
多线程就好比是把单线程的总量分成了多条线路同时进行,自然是要快很多,目前绝大多数的应用程序都是采用的多线程,掌握多线程编程是一个实战程序员应会的技能,但跨线程控制控件,会遇到问题,子线程控制主线程的控件,会容易造成死循环,在C#当中是采用委托来解决这一问题。



