iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#单线程和多线程的端口扫描器应用比较详解
  • 302
分享到

C#单线程和多线程的端口扫描器应用比较详解

2024-04-02 19:04:59 302人浏览 安东尼
摘要

目录一、准备工作二、端口扫描器(单线程)三、端口扫描器(多线程)四、总结本文章使用C#编程,制作一个端口扫描器,能够扫描本机有哪些端口开放了,并显示出来,分别使用单线程和多线程进行了

本文章使用C#编程,制作一个端口扫描器,能够扫描本机有哪些端口开放了,并显示出来,分别使用单线程和多线程进行了比较。

编译软件:Visual Studio 2019
编译环境:windows 10
使用语言:C#

一、准备工作

第一步:新建工程

创建新项目

选择 Windows 窗体应用。

输入项目名称(Port_Scanning),选择代码存储路径,然后点击创建。

第二步:控件摆放

使用控件按下图摆放。

table × 4个
textbox × 4个
progressBar × 1 个
button × 1个
注:图中红色的文字为控件的ID

修改属性:点击一下 textbox4 控件,将 ReadOnly 属性设置为 True ,这样这个文本框就只读了而不能修改,用于显示结果的。

其它的字体、大小等属性可以在 Font 处编辑。

二、端口扫描器(单线程)

第一步:编写代码

  • 摆放完毕后,在窗口设计界面内,双击 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 Port_Scanning
{
    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 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;
            }
            textBox4.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);
                //扫描到则显示到显示框
                textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
            }
            catch
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

下图为单线程程序的执行过程,整个流程都是依次进行的。

编译执行以下,看看结果。

第二步:执行结果

这里说明一下: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 Port_Scanning
{
    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;
        //定义端口状态数据(开放则为true,否则为false)
        private bool[] done = new bool[65526];
        private bool OK;

        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
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

这是代码的执行流程,可以更加直观的看到程序如何执行的,利于理解多线程的含义。

这里提一句,代码中的构造函数中:CheckForIllegalCrossThreadCalls = false;,这一句是直接跳过跨线程检查,如果程序不当,会造成死循环,建议使用委托 delegate ,网上有很多关于委托的讲解,我不太熟悉,经过几次试验后,程序执行的时候,输出显示的结果的先后顺序会有点不同,这一点需要改进。

第二步:执行结果

可以看到多线程的端口扫描器的速度要比单线程的快很多。

四、总结

多线程就好比是把单线程的总量分成了多条线路同时进行,自然是要快很多,目前绝大多数的应用程序都是采用的多线程,掌握多线程编程是一个实战程序员应会的技能,但跨线程控制控件,会遇到问题,子线程控制主线程的控件,会容易造成死循环,在C#当中是采用委托来解决这一问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: C#单线程和多线程的端口扫描器应用比较详解

本文链接: https://www.lsjlt.com/news/165420.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c语言怎么计算字符串长度
    在C语言中,可以使用内置的strlen()函数来计算字符串的长度。该函数位于string.h头文件中。以下是一个示例代码: #inc...
    99+
    2024-05-15
    c语言
  • c语言怎么判断三个数的大小
    可以通过比较三个数之间的大小关系来判断它们的大小。以下是一个示例代码: #include <stdio.h> int ...
    99+
    2024-05-15
    c语言
  • c++排序函数sort怎么使用
    在C++中,使用sort函数对容器中的元素进行排序,可以按照默认的升序排列,也可以自定义排序规则。 下面是一个简单的例子,对一个ve...
    99+
    2024-05-15
    c++
  • c++中string的compare函数怎么使用
    在C++中,可以使用string的compare函数来比较两个string对象的大小。该函数的原型如下: int compare(c...
    99+
    2024-05-15
    c++
  • c++中string的compare函数有什么用
    c++中string的compare函数用于比较两个字符串的大小关系。该函数返回一个整数值,表示两个字符串的比较结果。 具体来说,如...
    99+
    2024-05-15
    c++
  • c++中print函数的功能有哪些
    在C++中,通常使用std::cout来进行输出操作,而不是使用print函数。std::cout是C++标准库中的一个输出流对象,...
    99+
    2024-05-15
    c++
  • c语言数据结构怎么使用
    在C语言中,数据结构可以通过定义结构体来实现。以下是一些常见的数据结构的使用方法示例: 链表: #include <std...
    99+
    2024-05-15
    c语言
  • c++中print函数的用法是什么
    在C++中没有内置的print函数,但可以使用cout来输出内容到控制台。cout是C++标准库中的一个输出流对象,可以使用<...
    99+
    2024-05-15
    c++
  • c++中concept的用法是什么
    在C++20中,Concept是一种新的语言特性,用于定义类型要求和约束。Concept可以被用来约束函数模板、类模板和普通函数的参...
    99+
    2024-05-15
    c++
  • c++中concept的作用是什么
    在C++中,concept的作用是定义一种通用的约束,用于限制模板参数的类型范围。通过使用concept,可以在编译时对模板参数进行...
    99+
    2024-05-15
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作