目录
一、 UDP编程
UDP和TCP的区别
UDP和TCP的主要区别在是二者在如何实现信息的可靠传递方面存在不同,具体表现为以下几点:
▼UDP可靠性不如TCP
TCP包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传递其他消息,否则将一直等待,直到收到确认信息为止。与TCP不同,UDP并不提供数据传达的保证机制。如果在发送方到接收方的传递过程中出现数据包的丢失,协议本身并不能做出任何检测或提示。因此人们把UDP称为不可靠的传输协议。
▼UDP不能保证有序传输
UDP不能确保数据的发送和接收顺序。对于突发性的数据报,有可能会乱序。不过,事实上UDP的这种乱序基本上很少出现,通常只会在网络非常拥挤的情况下才有可能发生。
说了这么多UDP的缺点,那使用它还有什么价值么?
从应用角度考虑,UDP相比TCP的优势,主要表现在以下几个方面。
UDP速度比TCP要快
UDP有消息边界
UDP可以一对多传输
1、简单的C#控制台应用
使用VS2019 完成第一个C#程序,显示多行输出。
1.1 文件—>新建项目,选择C#,控制台。然后下一步
1.2 更改项目名字及位置,然后创建
1.3 进入程序编写
代码内容
for(int i=0;i<50;i++) //for循环,小于五十次就继续输出内容
Console.WriteLine("Hello World,Hello C#");//显示输出输出内容
Console.ReadKey(); //暂停程序,按下任意键继续
1.4运行结果
这样,入门C#控制台程序就完成了。
2、C#使用UdpClient类进行简单通信
2.1、创建一个控制台程序:服务端,步骤和前面一样。
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace ConsoleApp2
{/// <summary>
/// 服务端
/// </summary>
class UDPReceive
{
static void Main(string[] args)
{
//创建一个UdpClient对象,11000为接收端口
UdpClient udpReceive = new UdpClient(11000);
//设置远程主机,(IPAddress.Any, 0)代表接收所有IP所有端口发送的数据
IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);//或 IPEndPoint remoteIpEndPoint = null;
int i = 0;
while (true)
{
//监听数据,接收到数据后,把数据转换成字符串并输出
byte[] receiveBytes = udpReceive.Receive(ref remoteIpEndPoint);
string returnData = Encoding.UTF8.GetString(receiveBytes);
Console.WriteLine(++i + returnData.ToString());
}
udpReceive.Close();
}
}
}
2.2、新建一个控制台程序:客服端
代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace ConsoleApp1
{
/// <summary>
/// 客户端
/// </summary>
class UDPSender
{
static void Main(string[] args)
{
//创建一个UdpClient对象,0表示系统自动分配发送端口
//(若同时在本机运行服务端和客户端,则服务端接收和客户端发送需要使用不同端口,否则两个程序使用同一端口将引发冲突)
UdpClient udpSender = new UdpClient(0);
//连接到服务端并指定接收端口
udpSender.Connect("localhost", 11000);
//连接到子网广播地址并指定接收端口
//udpSender.Connect("192.168.1.255", 11000);
//(在使用TCP/IP协议的网络中,主机标识段全为1的IP地址为广播地址,广播地址传送给主机标识段所涉及的所有计算机。
//例如,对于192.168.1.0(255.255.255.0)网段,其广播地址为192.168.1.255(255的2进制即为11111111),
//当发出目的地址为192.168.1.255时,它将分发给该网段上的所有计算机。)
//把消息转换成字节流发送到服务端
byte[] sendBytes = Encoding.UTF8.GetBytes("Hello World,Hello C#");
string a;
a = Console.ReadLine(); //从控制台获取数据
for (int i = 0; i < 50; i++) //循环,客户端发送一条消息,服务端收到50条
{
udpSender.Send(Encoding.UTF8.GetBytes(a), Encoding.UTF8.GetBytes(a).Length);
//把发送内容转换成字节流,以及发送的字节长度
}
//关闭链接
udpSender.Close();
}
}
}
2.3、运行程序
需要先运行服务端,再运行客户端。否则客户端在服务端运行之前就已经发出数据,则服务端不会接收到数据。
▼客户端输入:
▼服务端接收:
这里是本机的客户端和服务端的连接,也可以连接其他电脑和设备。基本就更改一下IP地址,有需要的就关掉防火墙。
Scoket UDP编程的流程图
二、用C#编写一个简单的Form窗口程序
1、用VS2019建立一个窗口程序项目
▼文件—>新建项目
2、窗体设计
▼在视图—>工具箱中调出控件
▼拖动按钮进行布局
▼点击控件,右下角可以更改name
3、代码编写
▼在同一台电脑测试时所用的代码:
using System;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private UdpClient receiveUdpClient;//接收用
private UdpClient sendUdpClient;//发送用
private const int port = 8001;//和本机绑定的端口号
IPAddress ip;//本机ip
IPAddress remoteip;//远程主机ip
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
//获取本机可用IP地址
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress ipa in ips)
{
if (ipa.AddressFamily == AddressFamily.InterNetwork)
{
ip = ipa;
break;
}
}
//为了在同一台机器调试,此IP也作为默认远程IP
remoteip = ip;
//IPAddress remoteip = IPAddress.Parse("");
}
private void Form1_Load(object sender, EventArgs e)
{
//创建一个线程接收远程主机发来的信息
Thread myThread = new Thread(ReceiveData);
myThread.IsBackground = true;
myThread.Start();
}
//接收数据
private void ReceiveData()
{
IPEndPoint local = new IPEndPoint(ip, port);
receiveUdpClient = new UdpClient(local);
IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
try
{
//关闭udpClient 时此句会产生异常
byte[] receiveBytes = receiveUdpClient.Receive(ref remote);
string receiveMessage = Encoding.Unicode.GetString(
receiveBytes, 0, receiveBytes.Length);
listBox1.Items.Add("收到的消息:" + receiveMessage);
}
catch
{
break;
}
}
}
//点击发送按钮发送数据
private void button1_Click_1(object sender, EventArgs e)
{
//remoteip = IPAddress.Parse(txt_IPAddress.Text);
Thread myThread = new Thread(SendMessage);
myThread.IsBackground = true;
myThread.Start(textBox1.Text);
}
//发送消息
private void SendMessage(object obj)
{
string message = (string)obj;
sendUdpClient = new UdpClient(0);
byte[] bytes = Encoding.UTF8.GetBytes(message);
IPEndPoint iep = new IPEndPoint(remoteip, port);
try
{
sendUdpClient.Send(bytes, bytes.Length, iep);
listBox1.Items.Add("发送的消息:" + message);
textBox1.Clear();
}
catch (Exception ex)
{
listBox1.Items.Add("发送出错:" + ex.Message);
}
}
delegate void AddItemDelegate(ListBox listbox, string text);
private void AddItem(ListBox listbox, string text)
{
if (listbox.InvokeRequired)
{
AddItemDelegate d = AddItem;
//Control.Invoke 方法 (Delegate, Object[]):
//在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
listbox.Invoke(d, new object[] { listbox, text });
}
else
{
//Add:动态的添加列表框中的项
listbox.Items.Add(text);
//SelectedIndex属性获取单项选择ListBox中当前选定项的位置
//Count:列表框中条目的总数
listbox.SelectedIndex = listbox.Items.Count - 1;
//调用此方法等效于将 SelectedIndex 属性设置为-1。
//可以使用此方法快速取消选择列表中的所有项。
listbox.ClearSelected();
}
}
delegate void ClearTextBoxDelegate();
private void ClearTextBox()
{
if (textBox1.InvokeRequired)
{
ClearTextBoxDelegate d = ClearTextBox;
textBox1.Invoke(d);
}
else
{
textBox1.Clear();
textBox1.Focus();
}
}
private void button2_Click_1(object sender, EventArgs e)
{
this.Close();
}
}
}
这里提示一下🙄,如图如果这种些地方是“0引用”,那个就是这个控件的功能没有激活。需要到设计界面去双击相应的控件,激活后然后编写控制代码
▼不同电脑间通信程序代码
using System;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private UdpClient receiveUdpClient;//接收用
private UdpClient sendUdpClient;//发送用
private const int port = 10000;//和本机绑定的端口号
IPAddress ip = IPAddress.Parse("10.60.191.8");//本机ip
IPAddress remoteip = IPAddress.Parse("10.60.191.43");//远程主机ip
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
////获取本机可用IP地址
//IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
//foreach (IPAddress ipa in ips)
//{
// if (ipa.AddressFamily == AddressFamily.InterNetwork)
// {
// ip = ipa;
// break;
// }
//}
//为了在同一台机器调试,此IP也作为默认远程IP.
//- remoteip = ip;
//IPAddress remoteip = IPAddress.Parse("");
}
private void Form1_Load(object sender, EventArgs e)
{
//创建一个线程接收远程主机发来的信息
Thread myThread = new Thread(ReceiveData);
myThread.IsBackground = true;
myThread.Start();
}
//接收数据
private void ReceiveData()
{
IPEndPoint local = new IPEndPoint(ip, port);
receiveUdpClient = new UdpClient(local);
IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
try
{
//关闭udpClient 时此句会产生异常
byte[] receiveBytes = receiveUdpClient.Receive(ref remote);
string receiveMessage = Encoding.UTF8.GetString(
receiveBytes, 0, receiveBytes.Length);
listBox1.Items.Add("收到的消息:" + receiveMessage);
}
catch
{
break;
}
}
}
//点击发送按钮发送数据
private void button1_Click_1(object sender, EventArgs e)
{
//remoteip = IPAddress.Parse(txt_IPAddress.Text);
Thread myThread = new Thread(SendMessage);
myThread.IsBackground = true;
myThread.Start(textBox1.Text);
}
//发送消息
private void SendMessage(object obj)
{
string message = (string)obj;
sendUdpClient = new UdpClient(0);
byte[] bytes = Encoding.UTF8.GetBytes(message);
IPEndPoint iep = new IPEndPoint(remoteip, port);
try
{
sendUdpClient.Send(bytes, bytes.Length, iep);
listBox1.Items.Add("发送的消息:" + message);
textBox1.Clear();
}
catch (Exception ex)
{
listBox1.Items.Add("发送出错:" + ex.Message);
}
}
delegate void AddItemDelegate(ListBox listbox, string text);
private void AddItem(ListBox listbox, string text)
{
if (listbox.InvokeRequired)
{
AddItemDelegate d = AddItem;
//Control.Invoke 方法 (Delegate, Object[]):
//在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
listbox.Invoke(d, new object[] { listbox, text });
}
else
{
//Add:动态的添加列表框中的项
listbox.Items.Add(text);
//SelectedIndex属性获取单项选择ListBox中当前选定项的位置
//Count:列表框中条目的总数
listbox.SelectedIndex = listbox.Items.Count - 1;
//调用此方法等效于将 SelectedIndex 属性设置为-1。
//可以使用此方法快速取消选择列表中的所有项。
listbox.ClearSelected();
}
}
delegate void ClearTextBoxDelegate();
private void ClearTextBox()
{
if (textBox1.InvokeRequired)
{
ClearTextBoxDelegate d = ClearTextBox;
textBox1.Invoke(d);
}
else
{
textBox1.Clear();
textBox1.Focus();
}
}
private void button2_Click_1(object sender, EventArgs e)
{
this.Close();
}
}
}
简单来说,两个代码就如下区别。其他的部分都一样
4、运行结果
▼本机运行结果
编辑内容,点击发送
接收到得内容
▼两台电脑通信结果
三、wireshark抓包分析
1、安装并打开wireshark
运行程序与其他主机通信一下,然后进入找到刚刚通讯的IP号
2、帧的格式为: 前导码–>目的地址–>源地址–>协议类型–>数据包–>校核码,我们注重分析以下几个内容