扫描器和游戏客户端
一. 编写端口扫描器程序,分别采用单一进程和多线程方式,对比两者的效果。
1.1单线程
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApp3
{
public partial class Form1 : Form
{
private string hostAddress;
private int start;
private int end;
private int port;
public Form1()
{
InitializeComponent();
}
private void label5_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
try
{
textBox4.Clear();
label5.Text = "0%";
hostAddress = textBox1.Text;
start = Int32.Parse(textBox2.Text);
end = Int32.Parse(textBox3.Text);
if (decideAddress())
{
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;
Scan();
label5.Text = xian;
label5.Refresh();
progressBar1.Value = i;
}
textBox4.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
textBox1.ReadOnly = false;
textBox2.ReadOnly = false;
textBox3.ReadOnly = false;
}
private void Scan()
{
int portnow = port;
TcpClient objTCP = null;
try
{
objTCP = new TcpClient(hostAddress, portnow);
textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
}
catch
{
}
}
}
}
展示结果:
1.2多线程
代码:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApp4
{
public partial class Form1 : Form
{
//主机地址
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;
public Form1()
{
InitializeComponent();
//不进行跨线程检查
CheckForIllegalCrossThreadCalls = false;
}
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
{
//未扫描到,则会抛出错误
}
}
}
}
展示结果:
1.3比较总结
多线程优缺点:
同步应用程序的开发比较容易,但由于需要在上一个任务完成后才能开始新的任务,所以其效率通常比多线程应用程序低。如果完成同步任务所用的时间比预计时间长,应用程序可能会不响应。多线程处理可以同时运行多个过程。多线程技术使程序的响应速度更快,因为用户界面可以在进行其他工作的同时一直处于活动状态。当前没有进行处理的任务可以将处理器时间让给其他任务。占用大量处理时间的任务可以定期将处理器时间让给其他任务。可以随时停止任务。可以分别设置各个任务的优先级以优化性能。
单线程优缺点:
单线程的也就是程序执行时,所跑的程序路径(处理的东西)是连续顺序下来的,必须前面的处理好,后面的才会执行到。
由于时间片很短,这样给用户的感觉是同时有好多线程在执行。但是线程切换是有代价的,因此如果采用多进程,那么就需要将线程所隶属的该进程所需要的内存进行切换,这时间代价是很多的。而线程切换代价就很少,线程是可以共享内存的。所以采用多线程在切换上花费的比多进程少得多。但是,线程切换还是需要时间消耗的,所以采用一个拥有两个线程的进程执行所需要的时间比一个线程的进程执行两次所需要的时间要多一些。即采用多线程不会提高程序的执行速度,反而会降低速度,但是对于用户来说,可以减少用户的响应时间。上述结果只是针对单CPU,如果对于多CPU或者CPU采用超线程技术的话,采用多线程技术还是会提高程序的执行速度的。因为单线程只会映射到一个CPU上,而多线程会映射到多个CPU上,超线程技术本质是多线程硬件化,所以也会加快程序的执行速度。
二. 编写一个网游客户端,游戏服务器的校园内网IP地址为 10.1.230.41,端口为3900,采用TCP连接。
-
连接成功后,可以将服务器发来的消息不停地显示在 listbox 中;
-
客户端要发给服务器的数据,通过 textbox输入 或者点击button;
-
能够播放背景音乐;
-
每隔30秒,变换一次游戏背景图片。
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace gameClient
{
public partial class Form1 : Form
{
//分别创建 TcpClient 和 NetworkStream 的对象
TcpClient tcpClient;
NetworkStream stream;
public Form1()
{
InitializeComponent();
//设置音量调节器的初始位置
trackBar1.Value = 50;
label1.Text = "50";
axWindowsMediaPlayer1.Hide();
}
/**
进入游戏
*/
private void button2_Click(object sender, EventArgs e)
{
/**
连接服务器
*/
try
{
//实例化
tcpClient = new TcpClient();
//向指定的IP地址的服务器发出连接请求
tcpClient.Connect("10.1.230.41", 3900);
textBox2.AppendText("成功连接服务器!");
//获取网络传输流
stream = tcpClient.GetStream();
//接受数据并转化为字符串
byte[] data = new byte[1024];
int receive = stream.Read(data, 0, 1024);
string mess = Encoding.Default.GetString(data, 0, receive);
//去除字符串中的终端转义字符
mess = strDelete(mess);
//显示出来
textBox2.AppendText(mess);
}
catch
{
textBox2.AppendText("服务器未启动" + Environment.NewLine);
}
/*
播放背景音乐
*/
trackBar1.Value = 50;
label1.Text = "50";
//字符串储存音乐路径
string s = @"G:\CloudMusic\罗大佑,黄霑,徐克 - 沧海一声笑.flac";
axWindowsMediaPlayer1.settings.setMode("loop", true);
axWindowsMediaPlayer1.settings.volume = 50;
axWindowsMediaPlayer1.URL = s;
}
/*字符操作*/
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;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
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();
}
private void axWindowsMediaPlayer1_Enter(object sender, EventArgs e)
{
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
label1.Text = trackBar1.Value.ToString();
}
private int flag = 0;
private void timer1_Tick(object sender, EventArgs e)
{
flag++;
string picturePath = @"F:\网络通信编程\图片\" + flag + ".jpg";
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
pictureBox1.Image = Image.FromFile(picturePath);
if(flag == 4)
{
flag =0;
}
}
}
}
展示结果: