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 ,这是由于命令行输入时,是以换行表示输入结束。
运行结果如下。
在输入时候,按回车 = 点击发送按钮。