1.控制台程序使用 UDP 通信
实验内容:用C#、Java或python编写一个命令行/控制台的简单hello world程序,实现如下功能:
- 在屏幕上连续输出50行“hello cqjtu!重交物联2019级”
- 同时打开一个网络UDP 套接字,向另一台室友电脑发送这50行消息
创建项目
- 打开 VS2019 ,点击 “ 创建新项目 ”
- 进行条件筛选,选择 “ 控制台应用(.NET Framework) ” ,然后点击 “ 下一步 ”
- 创建
单输出代码
for (int i = 0; i < 50; i++)
{
Console.WriteLine("hello cqjtu!重交物联2019级");
}
System.Console.ReadKey();
运行结果:
使用 UDP 通信
目前最普遍的服务模式是 C/S 模式,所以需要编写一个客户端 client 和一个服务端 Server ,来实现通信。
我在我的电脑上运行一个客户端代码,在我室友的电脑上运行一个服务端的代码,就可以实现通信功能。
发送端
代码流程:
- 首先显示提示信息,等待使用人员操作;
- 做好连接准备,如:设置IP、端口号等;
- for 循环发送数据;
- 关闭端口;
- 显示提示信息,等待用户确认退出。
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
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("按下任意按键开始发送...");
Console.ReadKey();
UdpClient client = new UdpClient();
IPAddress remoteIP = IPAddress.Parse("10.60.4.53");
int remotePort = 11000;
IPEndPoint remotePoint = new IPEndPoint(remoteIP, remotePort);
for (int i = 0; i < 50; i++)
{
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.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
int result = 1;
UdpClient client = new UdpClient(11000);
string receiveString = null;
byte[] receiveData = null;
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();
}
}
}
结果
发送: 接收:
Form窗口程序使用 TCP 通信
用VS2017/2019 的C#编写一个简单的Form窗口程序,有一个文本textEdit和一个发送按钮button,运行程序后,可以在文本框里输入文字,如“hello cqjtu!重交物联2019级”,点击button,将这些文字发送给室友电脑,采用UDP套接字;
从客户端发送多条数据,服务器端接收多条数据,服务器端反馈发送信息给客户端,客户端收到并显示出来。
接下来,我们创建一个新的 C# Form 窗口程序。
创建客户端项目
客户端设计界面
从工具箱内拖 2 个 TextBox 和 1 个 Button 控件。
设置输入框属性:
- 选中对应的 TextBox ,并在右下角的属性中找到 Font 属性,并点击 “ … ”
里面可以设置自己喜欢的字体 - 设计属性中的(Name)这里默认的 textBox1 ,也可以更改,但不能重复,唯一标识,这是控件的编号,用于代码编写的时候识别,就像是身份证号一样,但是不能出现中文。
- 在布局这里,Location 是指控件左上角顶点基于窗口所在的位置,Size 是指控件的长和宽,可以自行设置。
设置消息显示界面属性:
- 点击黑色的小三角按钮,可以将单行文本框设置为多行文本框。
- 添加垂直滚动条:找到 ScrollBars 属性,设置参数为 Vertical 。
- 设置边界样式:找到 BorderStyle ,参数设置为 FixedSingle 。
- 设置消息显示界面的 TextBox 不可编辑:找到 Enabled 属性,参数设为 False 。
还可以根据设置消息输入框的文本样式来设置消息显示界面内的文本样式。
设置发送消息按钮属性:
- 找到 Text 属性,参数输入为 “ 发送 ” ,则控件上就会显示输入的字样。
设置窗体属性:
- 左击窗体选中它,在右下角的属性中找到 Text 属性,编辑为 “ 客户端 ” ,然后窗体的左上角,就显示为 “ 客户端 ”。
- 紧接着,有个 AcceptButton 属性,下拉框选中这个 button1 按钮,设置完这个属性后,当我们最后执行这个程序后,按下回车键 = 点击这个按钮。
客户端代码实现
双击图形界面的按钮后,VS2019 会自动的转到代码编辑区域。
代码流程: 当点击按钮后,button1_Click 函数开始执行。
- 获取当前时间,并打印到消息显示界面内;
- 做连接服务器端的准备,如:设置IP、设置端口号、实例socket端口;
- 打印连接信息,并连接服务器;
- 从消息输入框获取字符串并按照UTF8编码到字节数组存储,然后发送出去。
- 将从服务器端接收到的字节流按照UTF8解码为字符串并存储打印出来。
- 关闭socket端口变量。
两个 catch 是做异常情况处理,并打印到消息显示界面内。
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 Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
try
{
string str = "The current time: ";
str += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
textBox1.AppendText(str + Environment.NewLine);
int port = 2000;
string host = "10.61.170.2";
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);
Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
str = "Connect to server...";
textBox1.AppendText(str + Environment.NewLine);
c.Connect(ipe);
string sendStr = textBox2.Text;
str = "The message content: " + sendStr;
textBox1.AppendText(str + Environment.NewLine);
byte[] bs = Encoding.UTF8.GetBytes(sendStr);
str = "Send the message to the server...";
textBox1.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;
textBox1.AppendText(str + Environment.NewLine);
c.Close();
}
catch (ArgumentNullException f)
{
string str = "ArgumentNullException: " + f.ToString();
textBox1.AppendText(str + Environment.NewLine);
}
catch (SocketException f)
{
string str = "ArgumentNullException: " + f.ToString();
textBox1.AppendText(str + Environment.NewLine);
}
textBox1.AppendText("" + Environment.NewLine);
textBox2.Text = "";
}
}
}
服务器端代码
根据前面 “ 控制台程序使用 UDP 通信 ” 创建一个新的控制台程序
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);
}
}
}
}
}
运行
wireshark抓包
控制台程序使用 UDP 通信
输入 “ UDP ” 过滤包,然后就可以看到下面的信息,这些就是我发送给我室友电脑上的 50 条数据
- 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级
Form窗口程序使用 TCP 通信
UDP与TCP包的区别 UDP: 第三行:Internet Protocl Version 4:IPv4协议 第四行:User Datagram Protocol:UDP协议 TCP: 第三行:Internet Protocl Version 4:IPv4协议 第四行:Transmission Control Protocol:TCP协议
它们的区别也就在于第四行,第三行都是 IPv4 协议。
总结
UDP/TCP 套接字进行网络通信,都是基于 C/S 模式,一个客户端,一个服务器端,在使用套接字的时候,端口号、IP地址是必不可缺的
参考
C#使用TCP/UDP协议通信并用Wireshark抓包分析数据
|