c#验证udp协议通信及实现端口扫描器
一、控制台实现udp通信
本次采用c/s模式创建应用实现通信。
1)创建项目
在VS2019中选择创建新项目,选择.net framework控制台应用,设置项目名并选择项目存储位置,然后点击创建,即可建立一个基本的控制台应用。
2)编写服务端
获取udp连接代码如下
UdpClient client = new UdpClient();
//设置目标ip地址,127.0.0.1为本地回环,即自己发给自己
IPAddress remoteIP = IPAddress.Parse("127.0.0.1");
//设置端口号
int remotePort = 11000;
IPEndPoint remotePoint = new IPEndPoint(remoteIP, remotePort);
发送数据使用client.Send(sendData, sendData.Length, remotePoint);
,在发送之前需要使用Encoding.Default.GetBytes
把将发送的数据转为16进制编码格式。
完整main函数代码如下:
//提示信息
Console.WriteLine("按下任意按键开始发送...");
Console.ReadKey();
int m;
//做好链接准备
UdpClient client = new UdpClient(); //实例一个端口
IPAddress remoteIP = IPAddress.Parse("127.0.0.1"); //假设发送给这个IP
int remotePort = 11000; //设置端口号
IPEndPoint remotePoint = new IPEndPoint(remoteIP, remotePort); //实例化一个远程端点
for (int i = 0; i < 50; i++)
{
//要发送的数据:第n行:hello cqjtu!重交物联2019级
string sendString = null;
sendString += "第";
m = i + 1;
sendString += m.ToString();
sendString += "行:hello cqjtu!重交物联2019级";
//定义发送的字节数组
//将字符串转化为字节并存储到字节数组中
byte[] sendData = null;
sendData = Encoding.Default.GetBytes(sendString);
client.Send(sendData, sendData.Length, remotePoint);//将数据发送到远程端点
}
client.Close();//关闭连接
//提示信息
Console.WriteLine("");
Console.WriteLine("数据发送成功,按任意键退出...");
System.Console.ReadKey();
3)编写客户端
同样创建一个新项目,main函数内代码如下
int result;
string str = "第50行:hello cqjtu!重交物联2019级";
UdpClient client = new UdpClient(11000);
string receiveString = null;
byte[] receiveData = null;
//实例化一个远程端点,IP和端口可以随意指定,等调用client.Receive(ref remotePoint)时会将该端点改成真正发送端端点
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Any, 0);
Console.WriteLine("正在准备接收数据...");
while (true)
{
receiveData = client.Receive(ref remotePoint);//接收数据
receiveString = Encoding.UTF8.GetString(receiveData);
Console.WriteLine(receiveString);
result = String.Compare(receiveString, str);
if (result == 0)
{
break;
}
}
client.Close();//关闭连接
Console.WriteLine("");
Console.WriteLine("数据接收完毕,按任意键退出...");
System.Console.ReadKey();
4)测试
同时运行这两个应用,进行测试,测试结果如下:
二、图像界面实现发送信息
1).创建项目
选择下图所示项目模板,创建项目。打开后即进入界面设计。
在其中从工具栏中加入一个按钮,一个文本框和一个富文本框,按自己想法放置。
2)双击添加进的按钮。进入代码编辑界面。按钮点击代码如下
private void button1_Click(object sender, EventArgs e)
{
UdpClient client = new UdpClient();
IPAddress remoteIP = IPAddress.Parse("127.0.0.1");
int remotePort = 11000; //设置端口号
IPEndPoint remotePoint = new IPEndPoint(remoteIP, remotePort);
string text = textBox1.Text;
byte[] sendData = null;
sendData = Encoding.Default.GetBytes(text);
client.Send(sendData, sendData.Length, remotePoint);//将数据发送到远程端点
client.Close();//关闭连接
//提示信息
richTextBox1.Text+="数据已发送\n";
}
3)测试
测试结果如下:
3.端口扫描器实现
1)创建项目,设计界面
按前面所说创建桌面应用项目,界面设计如下:
2)输入端口信息获取及进度条相关配置
在代码中创建一个新函数。将端口相对应文本输入框的textchanged事件绑定为该函数。在class中创建两个私有成员变量,保存相关信息。代码如下
private void TextBox3_TextChanged(object sender, EventArgs e)
{
try {
progressBar1.Value = start;
}
catch
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 65535;
progressBar1.Value = 0;
}
string text= "-1" ;
TextBox temp= (TextBox)sender;
if(temp.Text.Length !=0)
text = temp.Text;
if (temp == textBox3 && !text.Equals("-1"))
{
label4.Text = text;
start = Int32.Parse(text);
}
else if (temp == textBox2 && !text.Equals("-1"))
{
label5.Text = text;
end = Int32.Parse(text);
progressBar1.Minimum = start;
progressBar1.Maximum = end;
if(end < start || end >65536 || start < 0 || start > 65536)
{
richTextBox1.Text += "\n输入有误,请重新输入!\n";
textBox3.Clear();
textBox2.Clear();
label4.Text = "";
label5.Text = "";
start = 0;
end = 0;
}
}
}
2.单线程扫描实现
双击单线程对应按钮,进入代码编辑界面。相关代码如下:
private void Clicked(object sender, MouseEventArgs e)
{
progressBar1.Value = start;
if (textBox1.Text == null)
{
richTextBox1.Text += "\n请输入ip地址\n";
return;
}
else
addr = textBox1.Text;
TcpClient obj = null;
richTextBox1.Text += "\n开始扫描!\n";
while (progressBar1.Value != progressBar1.Maximum) {
try {
obj = new TcpClient(addr, progressBar1.Value);
} catch
{
}
finally
{
progressBar1.Value += 1;
}
richTextBox1.Text += "\n扫描结束\n";
}
}
在实际使用时,如果使用单线程,等待时间将会较长,同时,扫描时也不能对该窗口进行任何操作,否则将会卡死,使用体验较差。
多线程扫描实现
先在class中定义两个私有线程变量。然后双击多线程对应按钮,进入代码编辑界面。
相关代码如下
private void button2_Click(object sender, EventArgs e)
{
progressBar1.Value = start;
if (textBox1.Text == null)
{
richTextBox1.Text += "\n请输入ip地址\n";
return;
}
else
addr = textBox1.Text;
richTextBox1.Text = "端口扫描器\n";
richTextBox1.Text += "\n开始扫描!\n";
Thread startscan = new Thread(new ThreadStart(scan));
startscan.Start();
}
scan:
public void scan()
{
bool flag = false;
for(int i = start; i <= end; i++)
{
port = i;
scans = new Thread(new ThreadStart(truescan));
scans.Start();
System.Threading.Thread.Sleep(100);
}
while(!flag){
for(int i = start; i <= end; i++)
{
if (!ischecked[i])
{
flag = false;
System.Threading.Thread.Sleep(100);
break;
}
flag = true;
}
}
End temp = new End(scanEnd);
this.Invoke(temp);
}
truescan:
public void truescan()
{
int temp = port;
Thread now = scans;
TcpClient obj = null;
try
{
obj = new TcpClient(addr, temp);
setText temp1 = new setText(portOpen);
this.Invoke(temp1,temp);
}
catch
{
setText temp1 = new setText(portNotOpen);
this.Invoke(temp1,temp);
}
finally
{
ischecked[temp] = true;
End temp1 = new End(gogo);
if(temp!=start)
this.Invoke(temp1);
}
}
在c#中,如果需要在某一线程中操作非本线程创建的控件时,需要使用委托来实现相关操作。本次定义使用的相关定义代码如下
public delegate void setText(int t);
public delegate void End();
在使用前,将这些通过new 与相关处理函数绑定,再在子线程中通过invoke调用,传递参数。本次定义的处理函数如下:
public void portOpen(int temp)
{
richTextBox1.Text += "\n端口" + temp + "开放\n";
}
public void portNotOpen(int temp)
{
richTextBox1.Text += "\n端口" + temp + "未开放\n";
}
public void scanEnd()
{
richTextBox1.Text += "扫描结束";
}
public void gogo()
{
progressBar1.Value++;
}
3)测试
测试结果如下
4.udp数据帧分析
使用前面的发送软件发送信息,通过抓包软件得到数据包如下
如果发送到其他电脑,第二行应与以下图片相似。
第一行为目标地址,第二行为源地址,第三行为类型,即采用的协议,第四行为校验码。
第三行为ip包。如下所示
包含以下内容
- Version(版本号):分为 IPv4 和 IPv6 这里用的 IPv4 ,所以值为 4 ,1 个字节
- Header length(ip报头长度):32位字的报头长度
- TOS(级别):服务类型描述数据报将如何被处理,比如优先发送等,大多数都是默认为 0
- Total Length(总长度):包括报头和数据的数据包长度
- identifier(标识):唯一的 IP 数据包值
- Flags(标志):说明是否有数据被分段,由于本次每个包的数据很小,没有被分段,所以这里数值为 0 。
- Fragmentation Offset(分段偏移):如果数据包在装人帧时太大,则需要进行分段和重组,这里没有分段,所以偏移量为 0
- TTL(存活期):存活期是在数据包产生时建立在其内部的一个设置,如果这个数据包在这个TTL到期时仍没有到达它要去的目的地,那么它将被丢弃,这个设置将防止IP包在寻找目的地的时候在网络中不断循环,每经过一个路由器,它的值就减一,这里它的值为 64 ,也就是 64 个生存期
- Protocol(协议):上层协议的端口( TCP 是端口 6;UDP 是端口 17) ,同样也支持网络层协议,如ARP和ICMP,这里值为 17 ,也就是 UDP 协议
- Header Checksum(校验码):只针对报头的循环冗余校验(CRC)
- 目的地址及源地址
最后一行为数据,里面是发送的数据字节,如果发送的数据含有中文,通过utf-8编码格式解码后即可看到完整数据。