练习TCP编程——端口编程与网游客户端

一、TCP通讯协议简介

百度比我专业多了
主要就是三次握手建立连接,然后就开始收发消息。比UDP安全,但是还是有漏洞,运气好可以拆出包。而且在第二次握手时,客户端如果不再发送第三次握手,会导致服务端一直处于等待状态。(如果是攻击服务器不知如此)

二、单线程TCP端口检测

1.界面设计
程序界面如图
在这里插入图片描述

2.程序主要代码

static void Main()
        {
           // Control.CheckForIllegalCrossThreadCalls = false;
            Application.Run(new Form1());
        }

        private void txtStart_TextChanged(object sender, System.EventArgs e)
        {
            //获取输入的起始端口值
            lblStart.Text = txtStart.Text;
        }
        private void txtEnd_TextChanged(object sender, System.EventArgs e)
        {
            //获取输入的接受端口值
            lblStop.Text = txtEnd.Text;
        }
        private void btnScan_Click(object sender, System.EventArgs e)
        {
            //显示端口扫描的范围
            progressBar1.Minimum = Int32.Parse(txtStart.Text);
            progressBar1.Maximum = Int32.Parse(txtEnd.Text);
            //显示框初始化
            lbResult.Items.Clear();
            lbResult.Items.Add("端口扫描器 v1.0.");
            lbResult.Items.Add("");
        }
        private void PortScan()
        {
            start = Int32.Parse(txtStart.Text);
            end = Int32.Parse(txtEnd.Text);
            //检查输入范围合法性
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
            {
                lbResult.Items.Add("开始扫描... (可能需要请您等待几分钟)");
                Addr = txtAddr.Text;
                for (int i = start; i <= end; i++)
                {
                    port = i;
                    不使用扫描线程
                    Scan();
                    progressBar1.Value = i;
                    lblNow.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);
                }
                lbResult.Items.Add("扫描结束!");
            }
            else
            {
                MessageBox.Show("输入错误,端口范围为[0-65536]");
            }
        }
        private void Scan()
        {
            int portnow = port;
            done[portnow] = true;
            //创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            //扫描端口,成功则写入信息
            try
            {
                //用TcpClient对象扫描端口
                objTCP = new TcpClient(Addr, portnow);
                lbResult.Items.Add("端口 " + portnow.ToString() + " 开放!");
            }
            catch
            {
            }
        }

3.效果
运行起来非常的慢,还会卡顿,扫描100个端口都需要几十分钟甚至更久。

三、多线程TCP端口检测

1.界面
界面设计没有变化
2.程序主要代码
在上面代码的基础上加入了线程操作。设计思路主要是运用TCP去不断连接对方的各个端口,连接失败则不返回,说明对方此端口并未开放,反之则开放了。可以用来检测端口的状态。
注意:直接跨线程操作,编译器会报错,Windows这样设计是为了一些错误。解决的办法有两种:
(1)像我这样加一句Control.CheckForIllegalCrossThreadCalls = false;关闭跨线程检查。因为这个程序不是很复杂,不会出现严重的不安全现象,可以关闭跨线程检查。这种方法最简单。
(2)使用安全的跨线程调用。这种方法安全有效,比较复杂。(参考资料里有介绍)

static void Main()
        {
            Application.Run(new Form1());
        }

        private void txtStart_TextChanged(object sender, System.EventArgs e)
        {
            //获取输入的起始端口值
            lblStart.Text = txtStart.Text;
        }
        private void txtEnd_TextChanged(object sender, System.EventArgs e)
        {
            //获取输入的接受端口值
            lblStop.Text = txtEnd.Text;
        }
        private void btnScan_Click(object sender, System.EventArgs e)
        {
            //创建线程,并创建ThreadStart委托对象
            Thread process = new Thread(new ThreadStart(PortScan));
            process.Start();
            //显示端口扫描的范围
            progressBar1.Minimum = Int32.Parse(txtStart.Text);
            progressBar1.Maximum = Int32.Parse(txtEnd.Text);
            //显示框初始化
            lbResult.Items.Clear();
            lbResult.Items.Add("端口扫描器 v1.0.");
            lbResult.Items.Add("");
        }
        private void PortScan()
        {
            //关闭跨线程检查
            Control.CheckForIllegalCrossThreadCalls = false;
            start = Int32.Parse(txtStart.Text);
            end = Int32.Parse(txtEnd.Text);
            //检查输入范围合法性
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
            {
                lbResult.Items.Add("开始扫描... (可能需要请您等待几分钟)");
                Addr = txtAddr.Text;
                for (int i = start; i <= end; i++)
                {
                    port = i;
                    //不使用扫描线程
                    //Scan();
                    //使用该端口的扫描线程
                    scanThread = new Thread(new ThreadStart(Scan));
                    scanThread.Start();
                    //使线程睡眠
                    System.Threading.Thread.Sleep(100);
                    progressBar1.Value = i;
                    lblNow.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);
                }
                lbResult.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);
                lbResult.Items.Add("端口 " + portnow.ToString() + " 开放!");
            }
            catch
            {
            }
        }

3.运行效果
运行之后,通过TCP扫描100个端口只需要几分钟,比单线程快了很多。
在这里插入图片描述

四、网游客户端

1.建立连接并显示

首先做好图形界面,然后要求点击“进入游戏”,能够连接到网游服务器,点击“退出游戏”能够退出连接,并且在输入框内输入,点击回车,能够向服务器发送此信息。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
程序主要代码
使用TCP连接到服务器,然后进行交互,也可以运用多线程来操作。

//接收数据函数多线程运用,注释部分
        /*private void ReceiveMsg()
        {
            while (true)
            {
                try
                {
                    //创建接受数据的字节流
                    byte[] getData = new byte[1024];
                    //从网络流中读取数据
                    ns.Read(getData, 0, getData.Length);
                    //将字节数组转换成文本形式
                    string getMsg = Encoding.Default.GetString(getData);
                    //将消息添加到richTextBox1中
                    richTextBox1.Text += getMsg;
                }
                catch (ThreadAbortException)
                {
                    //捕捉到线程被终止异常则表示是人为的断开TCP连接
                    //不弹出错误提示
                }
                catch (Exception e)
                {
                    //接受消息发生异常
                    MessageBox.Show(e.Message);
                    //并释放相关资源
                    if (ns != null)
                        ns.Dispose();
                    break;
                }
            }
        }*/
private void button_entergame_Click(object sender, EventArgs e)
        {//连接服务器进入游戏
            try
            {
                //初始化tcp
                tcpclient = new TcpClient();
                richTextBox1.Text += "正在连接。。。";
                //开始连接,服务器IP和端口号
                tcpclient.Connect("10.160.52.106",3900);
                //获得绑定的网络数据流
                ns = tcpclient.GetStream();
                //实例化并启动接受消息线程
               /*receiveThread = new Thread(ReceiveMsg);
                receiveThread.Start();*/
                //接收数据
                byte[] data = new byte[1024];
                //从网络流中读取数据
                ns.Read(data,0,data.Length);
                //将字节数组转换为文本
                string receive;
                receive = Encoding.Default.GetString(data);
                //显示在listbox中
                richTextBox1.AppendText(receive);
            }
            catch (Exception ex)//异常处理
            { MessageBox.Show(ex.Message); }
        }

        private void button_exitgame_Click(object sender, EventArgs e)
        {//退出游戏
            //断开TCP连接
            tcpclient.Close();
            //销毁绑定的网络数据流
            ns.Dispose();
            //销毁接受消息的线程
            //receiveThread.Abort();
        }

2.发送消息

private void textBox_input_KeyDown(object sender, KeyEventArgs e)
        {//发送信息
            try
            {
                //按下enter发送信息
                if (e.KeyCode == Keys.Enter)
                {
                    //从textBox1中获得数据
                    byte[] sendata = new byte[1024];
                    sendata = Encoding.Default.GetBytes(textBox_input.Text + '\n');
                    //将数据写入到网络数据流中
                    ns.Write(sendata, 0, sendata.Length);
                    richTextBox1.Text += Encoding.Default.GetString(sendata);
                    textBox_input.Clear();
                }
            }
            catch (Exception ex)
            { MessageBox.Show(ex.Message); }               
        }

3.播放背景音乐

播放音乐的方式有很多种,我使用的是较为简单的一种。

private void playmusic()
        {//播放音乐
             WMPLib.WindowsMediaPlayer player = new WMPLib.WindowsMediaPlayer();
             player.URL = @"D:\Musicdownload\Goose house - 光るなら.mp3";
             player.controls.play();
            //这种方式需要将.wav文件加入到项目资源中
            /*SoundPlayer sp = new SoundPlayer();
            sp.SoundLocation = @"music.wav";
            sp.PlayLooping();*/
        }

找到引用,一般在VS右边的解决方案资源管理器
在这里插入图片描述
然后如下操作
在这里插入图片描述
再敲写代码即可。

4.变换背景图片

实现图片的变换,可以在30s后变换以此背景图片。这需要使用到Timer容器。
在这里插入图片描述
用Timer布置在需要变换的pictureBox上,然后设置属性。
在这里插入图片描述
为了实验,我设置了3s(3000ms)的间隔。
下面是代码

private void timer1_Tick(object sender, EventArgs e)
        {
            picno++;
            //设置图片路径
            string picturePath = @"D:\QQdownload\Picture\a洛天依0" + picno +".jpg";
            pictureBox1.Image = Image.FromFile(picturePath);
            if (picno == 9)
            {
                picno = 0;
            }
        }

运行效果(部分)
在这里插入图片描述
在这里插入图片描述

五、参考

TCP编程又有同步和异步之分,这里只是使用了同步连接。
同步TCP客户端
异步TCP客户端
C#5种播放音乐的方式
微软关于跨线程问题的解答
如何用跨线程方式调用Windows窗体控件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值