TCP编程-端口扫描器与网游客户端

用C#实现端口扫描器小程序

一、UI 设计

1.用 VS2017 新建一个 Windows 窗体应用(.NET Framework) 的项目

2.点击进入 From.cs[设计],可将界面设计如下:

在这里插入图片描述

二、只用单一进程实现

1.核心代码

//用单一进程测试端口扫描
        private void main_Thread()
        {
            listBox1.Items.Add("开始扫描……(该过程可能会等待一定时间!)");
            for (int i = startPort; i <= endPort; i++)
            {
                progressBar1.Value = i;
                label5.Text = i.ToString();
                try
                {
                    TcpClient tcp = new TcpClient(IP, i);
                    listBox1.Items.Add("端口 " + i.ToString() + " 开放!");
                }
                catch
                {
                }
            }
        }

2.效果如下

在这里插入图片描述
由图可见,程序执行十分的卡顿,窗体界面已经不能拖动了。此时,应该用多线程的方式解决。

三、多线程的说明

线程: 是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

多线程: 是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的好处: 可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

四、用多线程方式实现

1.C# 实现多线程有多种方法,这里主要用 Thread类 实现

1)用 Thread类 创建线程

ThreadStart childref = new ThreadStart(CallToChildThread);
Thread childThread = new Thread(childref);
childThread.Start();

2.实现流程大致如下

在这里插入图片描述

3.核心代码

1)添加 button 按钮 click 事件

private void button_Click(object sender, EventArgs e)
        {
            //创建线程,并创建 ThreadStart 委托对象
            ThreadStart childScan = new ThreadStart(port_Scan);
            Thread childThread = new Thread(childScan);
            childThread.Start();
            //显示端口扫描范围
            progressBar1.Minimum = startPort;
            progressBar1.Maximum = endPort;
            //main_Thread();
            //显示初始化端口信息
            listBox1.Items.Clear();
            listBox1.Items.Add("欢迎使用端口扫描器 v1.0!!!");
        }

2)线程 childThread 传入的 ThreadStart 委托对象的返回类型为void的无参数方法 port_Scan()

private void port_Scan()
        {
            //检查端口合法性
            if((startPort>=0 && startPort<=65535) && (endPort>=0 && endPort<=65535) && (startPort <= endPort))
            {
                if(IP!="" && judge_IP())
                {
                    listBox1.Items.Clear();
                    listBox1.Items.Add("开始扫描……(该过程可能会等待一定时间!)");
                    for (int i=startPort; i <= endPort; i++)
                    {
                        port = i;
                        //创建该端口的扫描线程
                        ThreadStart childScan = new ThreadStart(Scan);
                        Thread scanThread = new Thread(childScan);
                        scanThread.Start();
                        //使线程暂停100ms
                        System.Threading.Thread.Sleep(100);
                        progressBar1.Value = i;
                        label5.Text = i.ToString();
                    }
                    bool flag = true;
                    while (flag)
                    {
                        int i;
                        for(i = startPort; i <= endPort; i++)
                        {
                            if (!done[i])
                            {
                                break;
                            }
                        }
                        if (i == endPort + 1)
                        {
                            flag = false;
                        }
                        //线程暂停2000ms
                        System.Threading.Thread.Sleep(2000);
                    }
                    listBox1.Items.Add("扫描结束!!!");
                }
                else
                {
                    MessageBox.Show("IP地址不合法!", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
                }
            }
            else
            {
                //错误消息反馈
                MessageBox.Show("端口不合法!", "Error", MessageBoxButtons.OKCancel,MessageBoxIcon.Error);
            }
        }

3)线程 scanThread传入的 ThreadStart 委托对象的返回类型为void的无参数方法 Scan()

private void Scan()
        {
            int portnow = port;
            //记录该端口已访问
            done[portnow] = true;
            try
            {
                TcpClient tcp = new TcpClient(IP, portnow);
                listBox1.Items.Add("端口 " + portnow.ToString() + " 开放!");
            }
            catch
            {
            }
        }

4.最终实现效果

在这里插入图片描述

C#编写网游客户端连接游戏服务器

一、准备工作

第一步:新建项目
新建一个窗体应用

在这里插入图片描述
新建完毕后,会转到窗体设计界面。

第二步:添加 WindowsMediaPlayer 控件
首先需要添加 Windows Media Player 控件(已有,则忽略),后续播放背景音乐需要使用到。
右击 “ 工具箱 ” 内的 “ 常规 ” ,点击 “ 选择项 ” 。

在这里插入图片描述
选择 “ COM 组件 ” ,勾选上 “ Windows Media Player ” ,再点击 “ 确定 ” 。

在这里插入图片描述
第三步:界面设计
textbox —— 2个(输入框、显示框)
button —— 3个(发送、进入游戏、退出游戏)
picturebox —— 1个(图片显示)
trackbar —— 1个(音量调节)
label —— 1个(音量显示)
groupbox —— 2个(控件容器)
WindowsMediaPlayer —— 1个(媒体播放器)

以下红色字体内容为控件的名字。

在这里插入图片描述

第四步:设置控件属性
右击控件,点击属性,即可进入到属性设置界面。
设置控件的位置和大小。

在这里插入图片描述
设置文本字体、大小、靠左显示等等。

在这里插入图片描述

二、连接服务器

实现功能:点击连接游戏按钮后,能连接服务器,若连接成功,则将服务器发来的消息不停地显示在 textbox 中;若连接失败,则在 textbox 中显示 “ 服务器未启动 ” 。

双击 “ 连接游戏 ” 按钮,转到代码编辑区。
在 button2_Click 函数体内,编写连接的代码,在函数体外定义两个全局变量,这里我简单写了个去除终端转义字符的函数 strDelete 。

//分别创建 TcpClient 和 NetworkStream 的对象
TcpClient tcpClient;
NetworkStream stream;

/*****************
 * 进入游戏
 *****************/
private void button2_Click(object sender, EventArgs e)
{
    /*
        * 连接服务器
        */
    try
    {
        //实例化
        tcpClient = new TcpClient();
        //向指定的IP地址的服务器发出连接请求
        tcpClient.Connect("10.160.52.106", 3900);
        //获取网络传输流
        stream = tcpClient.GetStream();
        //接受数据并转化为字符串
        byte[] data = new byte[1024];
        int receive = stream.Read(data, 0, 1024);
        string msg = Encoding.Default.GetString(data, 0, receive);
        //去除字符串中的终端转义字符
        msg = strDelete(msg);
        //显示出来
        textBox2.AppendText(msg);
    }
    catch
    {
        textBox2.AppendText("服务器未启动" + Environment.NewLine);
    }
}

/*****************
 * 去除终端转移字符
 *****************/
private string strDelete(string str)
{
    int flag = -1, de = 0;
    for (int i = 0; i < str.Length; i++)
    {
        if (str[i] == '')
        {
            flag = i;
        }
        if (flag != -1)
        {
            de++;
        }
        if (str[i] == 'm' && flag != -1)
        {
            str = str.Remove(flag, de);
            i = flag - 1;
            flag = -1;
            de = 0;
        }
    }
    return str;
}

运行结果如下。
在这里插入图片描述

三、传输数据

实现功能:客户端要发给服务器的数据,通过 textbox 输入,然后点击 button 发送出去,并将服务器端返回结果显示在 textbox 中。

在界面设计文件中,双击 “ 发送 ” 按钮,转到编辑代码的文件中,在 button1_Click 函数中编写以下的代码。

private void button1_Click(object sender, EventArgs e)
{
    //获取textBox1内的文本内容
    string msg = textBox1.Text + "\n";
    //将文本内容转化成比特流并发送给服务器
    byte[] data = new byte[1024];
    data = Encoding.Default.GetBytes(msg);
    stream.Write(data, 0, data.Length);
    //接收服务器端传来的数据流并转化为字符串
    byte[] data1 = new byte[1024];
    int receive = stream.Read(data1, 0, 1024);
    msg = Encoding.Default.GetString(data1, 0, receive);
    //去除字符串中的终端转义字符
    msg = strDelete(msg);
    //清除显示框之前的内容
    textBox2.Clear();
    //显示数据
    textBox2.AppendText(msg);
    //刷新输入框
    textBox1.Clear();
    //将光标集中到输入框中
    textBox1.Focus();
}

代码中,发送数据会加一个 \n ,这是由于命令行输入时,是以换行表示输入结束。
运行结果如下。
在输入时候,按回车 = 点击发送按钮。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值