TCP编程

实验目的

一. 编写端口扫描器程序,分别采用单一进程和多线程方式,对比两者的效果。

二. 编写一个网游客户端,游戏服务器的校园内网IP地址为 10.160.52.106,端口为3900,采用TCP连接。

  1. 连接成功后,可以将服务器发来的消息不停地显示在 listbox 中;

  2. 客户端要发给服务器的数据,通过 textbox输入 或者点击button;

  3. 能够播放背景音乐;

  4. 每隔30秒,变换一次游戏背景图片。

编写端口扫描器

  1. 创建项目文件
    在这里插入图片描述

  2. 布局
    在这里插入图片描述

  3. 单线程实现端口扫描,代码如下

using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace PortScan
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //自定义变量
        private int port;//记录当前扫描的端口号
        private string Address;//记录扫描的系统地址
        private bool[] done = new bool[65536];//记录端口的开放状态
        private int start;//记录扫描的起始端口
        private int end;//记录扫描的结束端口
        private bool OK;

        private void button1_Click(object sender, EventArgs e)
        {
            label4.Text = textBox2.Text;
            label6.Text = textBox3.Text;
            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  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("开始扫描:这个过程可能需要等待几分钟!");
                Address = textBox1.Text;
                for(int i = start; i <= end; i++)
                {
                    port = i;
                    Scan();
                    progressBar1.Value = i;
                    label5.Text = i.ToString();
                }
                while (!OK)
                {
                    OK = true;
                    for(int i = start; i <= end; i++)
                    {
                        if (!done[i])
                        {
                            OK = false;
                            break;
                        }
                    }
                }
                listBox1.Items.Add("扫描结束!");
            }
            else
            {
                MessageBox.Show("输入错误,端口范围为[0,65536]");
            }
        }
        //连接端口
        private void Scan()
        {
            int portnow = port;
            done[portnow] = true;
            TcpClient objTCP = null;
            try
            {
                objTCP = new TcpClient(Address, portnow);
                listBox1.Items.Add("端口"+portnow.ToString()+"开放");
            }
            catch
            {

            }

        }
    }
}

首先整个过程是依靠单条线程,先对输入开始端口到结束端口进行连接,连接成功,表示这个端口是开放的,并将其记录下来。然后就是将开放的端口在ListBox中显示出来。
在这里插入图片描述

  1. 多线程实现端口扫描,代码如下
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace PortScan
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //自定义变量
        private int port;//记录当前扫描的端口号
        private string Address;//记录扫描的系统地址
        private bool[] done = new bool[65536];//记录端口的开放状态
        private int start;//记录扫描的起始端口
        private int end;//记录扫描的结束端口
        private bool OK;
        private Thread scanThread;  
        //将输入的起始端口放到进度条的开始位置
        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 button1_Click(object sender, EventArgs e)
        {
            label4_TextChanged(sender, e);
            label6_TextChanged(sender, e);
            //创建线程,并创建ThreadStart委托对象
            Thread procss = new Thread(new ThreadStart(PortScan));
            procss.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 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("开始扫描:这个过程可能需要等待几分钟!");
                Address = textBox1.Text;
                for (int i = start; i <= end; i++)
                {
                    port = i;
                    //对该端口进行扫描的线程
                    scanThread = new Thread(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;
                        }
                    }
                }
                listBox1.Items.Add("扫描结束!");
                System.Threading.Thread.Sleep(1000);
            }
            else
            {
                MessageBox.Show("输入错误,端口范围为[0,65536]");
            }
        }
        private void Scan()
        {
            int portnow = port;
            //创建线程变量
            Thread Threadnow = scanThread;
            done[portnow] = true;
            //创建TcpClient对象,TcpClient用于TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            //扫描端口,成功就写入信息
            try
            {
                objTCP = new TcpClient(Address, portnow);
                listBox1.Items.Add("端口" + portnow.ToString() + "开放!");
            }
            catch
            {

            }
        }
    }
}


整个过程使用多个线程来实现扫描端口,次线程的进行并不会影响主线程的进行,线程之间不会相互影响。
在这里插入图片描述
在这里插入图片描述

编写网游客户端

  1. 新建项目
    在这里插入图片描述
  2. 设计界面
    在这里插入图片描述
  3. 键入代码到进入游戏按钮
        private void button2_Click(object sender, EventArgs e)
        {
            /*
                * 连接服务器
                */
            try
            {
                //实例化
                tcpClient = new TcpClient();
                //向指定的IP地址的服务器发出连接请求
                tcpClient.Connect("10.160.52.106", 4000);
                //获取网络传输流
                stream = tcpClient.GetStream();
                //接受数据并转化为字符串
                byte[] data = new byte[1024];
                int receive = stream.Read(data, 0, 1024);
                string msg = Encoding.Default.GetString(data, 0, receive);
                //显示出来
                textBox2.AppendText(msg);
            }
            catch
            {
                textBox2.AppendText("服务器未启动" + Environment.NewLine);
            }
        }
  1. 连接成功
    在这里插入图片描述
  2. 播放音乐,在构造函数中键入以下代码
public Form1()
{
    InitializeComponent();

    //设置音量调节器的初始位置
    trackBar1.Value = 50;
    label1.Text = "50";
    axWindowsMediaPlayer1.Hide();
}

然后在button1_Click函数键入以下代码

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);
    }

    /******************
     * 播放背景音乐
     ******************/
    //设置音量键值
    trackBar1.Value = 50;
    label1.Text = "50";
    //字符串存储音乐路径
    string s = @"C:\Users\ASUS\Music\damian.mp3";
    //设置为循环播放
    axWindowsMediaPlayer1.settings.setMode("loop", true);
    //设置初始音乐的音量大小(范围:0——100)
    axWindowsMediaPlayer1.settings.volume = 50;
    //设置播放歌曲的路径
    axWindowsMediaPlayer1.URL = s;
}

在trackBar1控件中键入以下代码

/*****************
 * 音量调节
 *****************/
private void trackBar1_Scroll(object sender, EventArgs e)
{
    //控制音量大小
    axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
    //控制音量显示
    label1.Text = trackBar1.Value.ToString();
}

在这里插入图片描述
在这里插入图片描述

  1. 变换背景图片
    加入timer控件,一定要让其父类为Forms。
    在这里插入图片描述
    设置Enabled 为 True
    设置 Interval 为 3000
    在这里插入图片描述
    在timer1_Tick中键入以下代码
//定义flag变量
private int flag = 0;

/*****************
 * 自动换图
 *****************/
private void timer1_Tick(object sender, EventArgs e)
{
    flag++;
    string picturePath = @"F:\121\" + flag + ".jpg";
    //设置图片填充
    pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
    pictureBox1.Image = Image.FromFile(picturePath);
    if (flag == 5)
    {
        flag = 0;
    }
}

在这里插入图片描述
在这里插入图片描述

总结

通过学习发现,单线程会出现卡顿的情况,而多线程的方式不会产生卡顿。单线程是一个端口一次进行扫描,多线程则是以线程的方式每个线程进行一个端口的扫描,很明显可以看出多线程扫描要比单线程扫描用的时间更少。
还学会了如何通过 C# 的 TcpClient 类建立连接,并使用 NetworkStream 类传输数据。

参考

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值