目录
一、UDP通信
1、VS创建项目
选择C#—>控制台应用,然后输入项目名称,选择位置 ,点击确定
2、代码
发送端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace UDP
{
class Program
{
static void Main(string[] args)
{
//提示信息
Console.WriteLine("按下任意按键开始发送...");
Console.ReadKey();
//做好链接准备
UdpClient client = new UdpClient(); //实例一个端口
IPAddress remoteIP = IPAddress.Parse("10.61.41.48"); //假设发送给这个IP
int remotePort = 11000; //设置端口号
IPEndPoint remotePoint = new IPEndPoint(remoteIP, remotePort); //实例化一个远程端点
for (int i = 0; i < 50; i++)
{
//要发送的数据:第n行:hello cqjtu!重交物联2018级
string sendString = null;
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();
}
}
}
接收端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace UDP
{
class Program
{
static void Main(string[] args)
{
int result = 1;
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.Default.GetString(receiveData);
Console.WriteLine(receiveString);
if (result == 50)
{
break;
}
result++;
}
client.Close();//关闭连接
Console.WriteLine("");
Console.WriteLine("数据接收完毕,按任意键退出...");
System.Console.ReadKey();
}
}
}
3、结果
二、Form窗口程序
1、创建新项目
2、界面设计
(1)从工具箱中选择button和textbox控件
(2)textbox选择多行文本
(3)设置边界样式
找到 BorderStyle ,参数设置为 FixedSingle
(4)设置消息显示界面的 TextBox 不可编辑
找到 Enabled 属性,参数设为 False
(5)设置按钮名称
(6)设置窗体
窗体text设置为客户端
找到AcceptButton 属性,下拉框选中这个 button1 按钮
3、代码
(1)客户端代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
try
{
/*
* 显示当前时间
*/
string str = "The current time: ";
str += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
textBox2.AppendText(str + Environment.NewLine);
/*
* 做好连接准备
*/
int port = 2000;
string host = "10.61.170.2";//我室友的IP地址
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);//把ip和端口转化为IPEndPoint实例
Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个Socket
/*
* 开始连接
*/
str = "Connect to server...";
textBox2.AppendText(str + Environment.NewLine);
c.Connect(ipe);//连接到服务器
/*
*发送消息
*/
string sendStr = textBox1.Text;
str = "The message content: " + sendStr;
textBox2.AppendText(str + Environment.NewLine);
byte[] bs = Encoding.UTF8.GetBytes(sendStr);
str = "Send the message to the server...";
textBox2.AppendText(str + Environment.NewLine);
c.Send(bs, bs.Length, 0);//发送信息
/*
* 接收服务器端的反馈信息
*/
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = c.Receive(recvBytes, recvBytes.Length, 0);//从服务器端接受返回信息
recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
str = "The server feedback: " + recvStr;//显示服务器返回信息
textBox2.AppendText(str + Environment.NewLine);
/*
* 关闭socket
*/
c.Close();
}
catch (ArgumentNullException f)
{
string str = "ArgumentNullException: " + f.ToString();
textBox2.AppendText(str + Environment.NewLine);
}
catch (SocketException f)
{
string str = "ArgumentNullException: " + f.ToString();
textBox2.AppendText(str + Environment.NewLine);
}
textBox2.AppendText("" + Environment.NewLine);
textBox1.Text = "";
}
}
}
(2)服务端代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
int i = 0;
int port = 2000;
string host = "10.61.170.2";
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Bind(ipe);
while (true)
{
i++;
try
{
Console.WriteLine("\t-----------------------------------------------");
Console.Write("Perform operations {0} :", i);
s.Listen(0);
Console.WriteLine("1. Wait for connect...");
Socket temp = s.Accept();
Console.WriteLine("2. Get a connect");
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = temp.Receive(recvBytes, recvBytes.Length, 0);
recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
Console.WriteLine("3. Server Get Message:{0}", recvStr);
string sendStr = "Ok!Client send message sucessful!";
byte[] bs = Encoding.UTF8.GetBytes(sendStr);
temp.Send(bs, bs.Length, 0);
temp.Close();
Console.WriteLine("4. Completed...");
Console.WriteLine("-----------------------------------------------------------------------");
Console.WriteLine("");
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
}
}
}
}
4、结果
三、端口扫描
1、单线程
using System;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Threading;
namespace MultithreadingScanningPort
{
public partial class Form1 : Form
{
private bool[] ports = new bool[65536];
public Form1()
{
InitializeComponent();
panel5.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
if(int.Parse(beginPortText.Text)<0 || int.Parse(beginPortText.Text) > int.Parse(endPortText.Text) || int.Parse(endPortText.Text)>65565)
{
messages.Items.Add("端口错误!");
return;
}
messages.Items.Clear();
messages.Items.Add("开始扫描.......");
ScanningPort();
}
public void ScanningPort() {
int start = int.Parse(beginPortText.Text);
int end = int.Parse(endPortText.Text);
messages.Items.Add("起始端口"+start);
messages.Items.Add("结束端口" + end);
for (int i = start; i <= end; i++)
{
Scanning(i);
}
messages.Items.Add("端口扫描结束");
}
public void Scanning(int port) {
this.ports[port] = true;
try {
TcpClient tmp = new TcpClient(ipAddressText.Text, port);
messages.Items.Add("端口" + port + "开放");
}
catch(System.Exception ex)
{
}
}
}
}
2、多线程
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SingleThreadScanningPort
{
public partial class Form1 : Form
{
private bool[] ports = new bool[65536];//所有端口号
private static int port=0;//当前端口号
private static int count = 0;//开放端口号数量
public Form1()
{
InitializeComponent();
//CheckForIllegalCrossThreadCalls设置为false;然后就能安全的访问窗体控件
CheckForIllegalCrossThreadCalls = false;
//初始化进度显示为空
label2.Text = "";
//停止扫描按钮为不可用
stopScanning.Enabled = false;
}
private void beginScanning_Click(object sender, EventArgs e)
{
//检查端口号
if (int.Parse(beginPortText.Text) < 0 || int.Parse(beginPortText.Text) > int.Parse(endPortText.Text) || int.Parse(endPortText.Text) > 65565)
{
messages.Items.Add("端口错误!");
return;
}
//新建线程执行扫描端口函数
Thread procss = new Thread(new ThreadStart(ScanningPort));
procss.Start();
//设置进度条最大值最小值分别为结束端口和起始端口
progressBar1.Maximum = int.Parse(endPortText.Text) - int.Parse(beginPortText.Text);
progressBar1.Minimum = 0;
//判断是否为继续扫描
if (port == 0)
{
messages.Items.Clear();
messages.Items.Add("开始扫描.......");
}
else
messages.Items.Add("继续扫描......");
//开始扫描禁用,停止扫描启用
beginScanning.Enabled = false;
stopScanning.Enabled = true;
}
public void ScanningPort()
{
int start;
int end = int.Parse(endPortText.Text);
//判断是否为继续扫描,如果是则继续扫描,否则重新扫描
if (port != 0)
start = port;
else
start = int.Parse(beginPortText.Text);
messages.Items.Add("起始端口" + start);
messages.Items.Add("结束端口" + end);
for (int i = start; i <= end; i++)
{
//按下停止扫描后开始扫描按钮启用,此时停止扫描
if (beginScanning.Enabled)
break;
port = i;
//新建线程进行扫描
Thread thread = new Thread(Scanning);
thread.Start();
//主线程休眠10ms
System.Threading.Thread.Sleep(10);
//修改进度条的值
progressBar1.Value = i- int.Parse(beginPortText.Text);
//显示端口号以及进度
label2.Text = "正在扫描端口: " + i+" 进度: "+Math.Round(( (i - int.Parse(beginPortText.Text)) *100.0 / progressBar1.Maximum),2)+"%";
progressBar1.PerformStep();
}
if (port != 0)
beginScanning.Text = "继续扫描";
else
{
messages.Items.Add("端口扫描结束");
messages.Items.Add("共有 " + count + " 个端口开放");
}
beginScanning.Enabled = true;
stopScanning.Enabled = false;
//判断是否扫描完毕
if (int.Parse(endPortText.Text) == port)
{
port = 0;
beginScanning.Text = "开始扫描";
}
}
public void Scanning()
{
this.ports[port] = true;
try
{
TcpClient tmp = new TcpClient(ipAddressText.Text, port);
messages.Items.Add("端口" + port + "开放");
count++;
}
catch (System.Exception ex)
{
}
}
private void stopScanning_Click(object sender, EventArgs e)
{
//按下停止按钮后,开始按钮和停止按钮状态翻转
beginScanning.Enabled = true;
stopScanning.Enabled = false;
}
}
}
3、结果对比
单线程操作的时候会出现界面直接卡死并且扫描速度很慢,
多线程操作扫描速度大大提升而且不会出现界面卡死
四、抓包分析
Version(版本号):分为 IPv4 和 IPv6 现在普遍都用的 IPv4 ,所以值为 4 ,1 个字节;
HLen(ip报头长度):32位字的报头长度(HLEN);
TOS(级别):服务类型描述数据报将如何被处理,比如优先发送等,大多数都是默认为 0 ;
Datagram Total Length(总长度):包括报头和数据的数据包长度
identifier(标识):唯一的 IP 数据包值;
Flags(标志):说明是否有数据被分段,我是一条一条的发送多个数据包,每个包的数据很小,没有被分段,所以这里数值为 0 。
Fragmentation Offset(分段偏移):如果数据包在装人帧时太大,则需要进行分段和重组,这里没有分段,所以偏移量为 0 ;
TTL(存活期):存活期是在数据包产生时建立在其内部的一个设置,如果这个数据包在这个TTL到期时仍没有到达它要去的目的地,那么它将被丢弃,这个设置将防止IP包在寻找目的地的时候在网络中不断循环,每经过一个路由器,它的值就减一,这里它的值为 128 ,也就是 128 个生存期;
Protocol(协议):上层协议的端口( TCP 是端口 6;UDP 是端口 17) ,同样也支持网络层协议,如ARP和ICMP,这里值为 17 ,也就是 UDP 协议;
Header Checksum(校验码):只针对报头的循环冗余校验(CRC);
Source Address(源地址):消息发送者的 ip 地址,这里是10.60.191.19;
Destination Address(目的地址):消息接收者的 ip 地址,这里是10.60.202.32;
ip包头的第六行:用于网络检测、调试、安全以及更多的内容,不过大多数情况都是默认为 0 的;
Data数据包:可以很显然看到,长度为 34 字节,对应的十六进制就是蓝色的区域了,这就是我们要发送的数据:第n行:hello cqjtu!重交物联2019级
五、总结
本次 实验总体来说内容还是比较多的,包含用C#写一个控制台程序,通过UDP发送给另一台电脑相关信息,以及通过可视化窗口的形式再次实现上述要求,并编写端口扫描程序,再进行抓包,总体来说还是有一定难度,在网上找了不少的参考资料,收获较多。