C#使用TCP/UDP协议通信并用Wireshark抓包分析数据

一、控制台程序使用UDP通信

1.创建新项目

(1)打开visual studio 2019,创建新项目
在这里插入图片描述
(2)选择控制台应用(.net.Framework)
在这里插入图片描述
(3)编辑项目名称,选择保存位置。
在这里插入图片描述
(4)创建好项目如下图所示
在这里插入图片描述

2.编写代码

在控制台上简单输出:

在 Main 函数内书写如下的代码(功能:连续输出 50 行数据)

for(int i = 0; i < 50; i++)
            {
                Console.WriteLine("第{0}行:hello cqjtu!重交物联2019级", (i + 1));
            }
            System.Console.ReadKey();

编译运行结果:

使用UDP通信
(1)目前最普遍的服务模式是 C/S 模式,所以需要一个客户端 client 和一个服务端 Server ,来实现通信。

(2)比如我现在在我的电脑上运行一个客户端代码,在我室友的电脑上运行一个服务端的代码,就可以实现通信功能。

(3)在我自己的电脑上使用 VS2019 创建一个新项目 client ,并将下列代码复制粘贴进去。(注意头文件!!!使用网络协议需要引入头文件 .Net 和 .Net.Sockets)
客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            //提示信息
            Console.WriteLine("按下任意按键开始发送...");
            Console.ReadKey();

            int m;

            //做好链接准备
            UdpClient client = new UdpClient();  //实例一个端口
            IPAddress remoteIP = IPAddress.Parse("192.168.43.15");  //假设发送给这个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 += "第";
                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();

        }
    }
}

流程:
(1)首先显示提示信息,等待使用人员操作
(2)做好连接准备,如:设置IP地址,端口号
(3)for循环发送数据
(4)关闭端口
(5)显示提示信息,等待用户确认退出

在我室友的电脑上使用 VS2019 创建一个新项目 server,并将下列代码复制粘贴进去。
服务器端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace Server1
{
    class Program
    {
        static void Main(string[] args)
        {
            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.Default.GetString(receiveData);
                Console.WriteLine(receiveString);
                result = String.Compare(receiveString, str);
                if (result == 0)
                {
                    break;
                }
            }
            client.Close();//关闭连接
            Console.WriteLine("");
            Console.WriteLine("数据接收完毕,按任意键退出...");
            System.Console.ReadKey();
        }
    }
}

代码流程:

①做好连接准备,并设置结束标志;
②循环接收数据;
③关闭连接;
④显示提示信息,等待用户确定退出。

运行结果显示
在这里插入图片描述
在这里插入图片描述

(1)打开wirshark,点击捕获—>选项在这里插入图片描述
(2)选择WLAN,开始
在这里插入图片描述
(3)可以看到现在 Wireshark 不断的在抓包,先点击红色的按钮暂停抓包。
在这里插入图片描述
(4)重新编译客户端和服务器端,先不要按下按键发送数据,先挂着,看下一步。
在这里插入图片描述
(5)点击鲨鱼鱼鳍的图标,然后点击 “ 继续不保存 ”,不保存之前抓取的包。
在这里插入图片描述
(6)然后按键盘开始发送数据,发送完后,点击 Wireshark 的红色按钮,停止抓包。

二、Form窗口程序使用 TCP 通信

1.创建新项目

(1)选中 “ Windows 窗体应用 ” ,再点击 “ 下一步 ”。
在这里插入图片描述
(2)设置项目名称、保存位置,再点击 “ 创建 ” 。
在这里插入图片描述
(3)创建完毕。
在这里插入图片描述

2.设计图形界面

摆放控件:

(1)首先往图形界面内拖动控件并进行摆放,如下图所示。
从工具箱内拖 2 个 TextBox 和 1 个 Button 控件。
在这里插入图片描述
设置消息输入框属性:
(2)左键选中最下面的 TextBox ,并在右下角的属性中找到 Font 属性,并点击 “ … ”
在这里插入图片描述
在这里插入图片描述
(3)然后在设计属性中的(Name)这里默认的 textBox1 ,也可以更改,但不能重复,唯一标识,这是控件的编号,用于代码编写的时候识别,就像是身份证号一样,不能出现中文。
在这里插入图片描述
(4)在布局这里,Location 是指控件左上角顶点基于窗口所在的位置,Size 是指控件的长和宽,可以自行设置。
也可自行拖动文本框,改变长宽,位置。
在这里插入图片描述
(5)选中TextBox2
添加垂直滚动条:找到 ScrollBars 属性,设置参数为 Vertical
在这里插入图片描述
设置边界样式:找到 BorderStyle ,参数设置为 FixedSingle
在这里插入图片描述
编号为 textBox2 。
在这里插入图片描述
设置消息显示界面的 TextBox 不可编辑:找到 Enabled 属性,参数设为 False 。
在这里插入图片描述
设置发送消息按钮属性:
左键点击选中按钮,找到 Text 属性,参数输入为 “ 发送 ” ,则控件上就会显示输入的字样
在这里插入图片描述
它的唯一标识为:button1
在这里插入图片描述
设置窗体属性:
(1)左击窗体选中它,在右下角的属性中找到 Text 属性,编辑为 “ 客户端 ” ,然后窗体的左上角,就显示为 “ 客户端 ”。
(2)紧接着,有个 AcceptButton 属性,下拉框选中这个 button1 按钮,设置完这个属性后,当我们最后执行这个程序后,按下回车键 = 点击这个按钮。
在这里插入图片描述
在这里插入图片描述

至此,控件的一些简单属性就设置完毕了。
在这里插入图片描述

3.编写代码

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();
        }

         public void button1_Click(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 = 82569;
                string host = "192.168.43.14";//我室友的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 = "";
        }
    }
}

**流程:

当点击按钮后,button1_Click 函数开始执行。
①获取当前时间,并打印到消息显示界面内;
②做连接服务器端的准备,如:设置IP、设置端口号、实例socket端口;
③打印连接信息,并连接服务器;
④从消息输入框获取字符串并按照UTF8编码到字节数组存储,然后发送出去。
⑤将从服务器端接收到的字节流按照UTF8解码为字符串并存储打印出来。
⑥关闭socket端口变量。
两个 catch 是做异常情况处理,并打印到消息显示界面内。
接下来开始编写服务器端代码:

注:从第三部分: “ Form窗口程序使用 TCP 通信 ” 开始至此,都是在编写客户端的部分,接下来我们需要编写服务器端的代码了。

根据第二部分: “ 控制台程序使用 UDP 通信 ” 创建一个新的控制台程序,这里就不再演示了。
复制下面 main 函数内的代码,并粘贴进你自己的 main 函数体内。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
             * 做好连接准备
             */
            int i = 0;
            int port = 82569;
            string host = "192.168.43.14";
            IPAddress ip = IPAddress.Parse(host);
            IPEndPoint ipe = new IPEndPoint(ip, port);
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个Socket类
            s.Bind(ipe);//绑定2000端口
            /*
             * 循环监听并处理消息
             */
            while (true)
            {
                i++;
                try
                {
                    Console.Write("Perform operations {0} :",i);
                    Console.WriteLine("\t-----------------------------------------------");
                    s.Listen(0);//开始监听
                    Console.WriteLine("1. Wait for connect...");
                    /*
                     * 实例一个新的socket端口
                     */
                    Socket temp = s.Accept();//为新建连接创建新的Socket。
                    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("");
                    //s.Close();//关闭socket(由于再死循环中,所以不用写,但如果是单个接收,实例socket并完成任务后需关闭)
                }
                catch (ArgumentNullException e)
                {
                    Console.WriteLine("ArgumentNullException: {0}", e);
                }
                catch (SocketException e)
                {
                    Console.WriteLine("SocketException: {0}", e);
                }
            }
        }
    }
}

4.编译客户端和服务器端

客户端
在这里插入图片描述
服务器端:
在这里插入图片描述
现在向服务器发送数据
在这里插入图片描述
服务器端
在这里插入图片描述

三、TCP编程-端口扫描器

1.创建新项目

在这里插入图片描述

2.设置界面

需要1个button,1个Listbox,4个TextBox,5个label控件
在这里插入图片描述

3.编写代码

单线程

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp3
{
    public partial class Form1 : Form
    {
        //主机地址
        private string hostAddress;
        //起始端口
        private int start;
        //终止端口
        private int end;
        //端口号
        private int port;
        //定义线程对象
        //private Thread scanThread;
        public Form1()
        {
            InitializeComponent();
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void label3_Click(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {

                //初始化
                textBox4.Clear();
                label5.Text = "0%";
                //获取ip地址和始末端口号
                hostAddress = textBox1.Text;
                start = Int32.Parse(textBox2.Text);
                end = Int32.Parse(textBox3.Text);
                if (decideAddress())
                {
                    //让输入的textbox只读,无法改变
                    textBox1.ReadOnly = true;
                    textBox2.ReadOnly = true;
                    textBox3.ReadOnly = true;
                    //设置进度条的范围
                    progressBar1.Minimum = start;
                    progressBar1.Maximum = end;
                    //显示框显示
                    textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
                    //调用端口扫描函数
                    PortScan();
                }
                else
                {
                    //若端口号不合理,弹窗报错
                    MessageBox.Show("输入错误,端口范围为[0-65536]!");
                }
            }
            catch
            {
                //若输入的端口号为非整型,则弹窗报错
                MessageBox.Show("输入错误,端口范围为[0-65536]!");
            }
        }

        private bool decideAddress()
        {
            //判断端口号是否合理
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
                return true;
            else
                return false;
        }

        private void PortScan()
        {
            double x;
            string xian;
            //显示扫描状态
            textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
            //循环抛出线程扫描端口
            for (int i = start; i <= end; i++)
            {
                x = (double)(i - start + 1) / (end - start + 1);
                xian = x.ToString("0%");
                port = i;
                //调用端口i的扫描操作
                Scan();
                //进度条值改变
                label5.Text = xian;
                label5.Refresh();
                progressBar1.Value = i;
            }
            textBox1.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
            //输入框textbox只读属性取消
            textBox1.ReadOnly = false;
            textBox2.ReadOnly = false;
            textBox3.ReadOnly = false;
        }

        private void Scan()
        {
            int portnow = port;
            //创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            try
            {
                //用于TcpClient对象扫描端口
                objTCP = new TcpClient(hostAddress, portnow);
                //扫描到则显示到显示框
                textBox1.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
            }
            catch
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

在这里插入图片描述
多线程
代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApp4
{
    public partial class Form1 : Form
    {
        //主机地址
        private string hostAddress;
        //起始端口
        private int start;
        //终止端口
        private int end;
        //端口号
        private int port;
        //定义线程对象
        private Thread scanThread;
        //定义端口状态数据(开放则为true,否则为false)
        private bool[] done = new bool[65526];
        private bool OK;
        public Form1()
        {
            InitializeComponent();
            //不进行跨线程检查
            CheckForIllegalCrossThreadCalls = false;
        }

        private void textBox4_TextChanged(object sender, EventArgs e)
        {

        }

        private void progressBar1_Click(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                //初始化
                textBox4.Clear();
                label5.Text = "0%";
                //获取ip地址和始末端口号
                hostAddress = textBox1.Text;
                start = Int32.Parse(textBox2.Text);
                end = Int32.Parse(textBox3.Text);
                if (decideAddress())
                {
                    textBox1.ReadOnly = true;
                    textBox2.ReadOnly = true;
                    textBox3.ReadOnly = true;
                    //创建线程,并创建ThreadStart委托对象
                    Thread process = new Thread(new ThreadStart(PortScan));
                    process.Start();
                    //设置进度条的范围
                    progressBar1.Minimum = start;
                    progressBar1.Maximum = end;
                    //显示框显示
                    textBox4.AppendText("端口扫描器 v1.0.0" + Environment.NewLine + Environment.NewLine);
                }
                else
                {
                    //若端口号不合理,弹窗报错
                    MessageBox.Show("输入错误,端口范围为[0-65536]!");
                }
            }
            catch
            {
                //若输入的端口号为非整型,则弹窗报错
                MessageBox.Show("输入错误,端口范围为[0-65536]!");
            }
        }

        private bool decideAddress()
        {
            //判断端口号是否合理
            if ((start >= 0 && start <= 65536) && (end >= 0 && end <= 65536) && (start <= end))
                return true;
            else
                return false;
        }

        private void PortScan()
        {
            double x;
            string xian;
            //显示扫描状态
            textBox4.AppendText("开始扫描...(可能需要请您等待几分钟)" + Environment.NewLine + Environment.NewLine);
            //循环抛出线程扫描端口
            for (int i = start; i <= end; i++)
            {
                x = (double)(i - start + 1) / (end - start + 1);
                xian = x.ToString("0%");
                port = i;
                //使用该端口的扫描线程
                scanThread = new Thread(new ThreadStart(Scan));
                scanThread.Start();
                //使线程睡眠
                System.Threading.Thread.Sleep(100);
                //进度条值改变
                label5.Text = xian;
                progressBar1.Value = i;
            }
            while (!OK)
            {
                OK = true;
                for (int i = start; i <= end; i++)
                {
                    if (!done[i])
                    {
                        OK = false;
                        break;
                    }
                }
                System.Threading.Thread.Sleep(1000);
            }
            textBox4.AppendText(Environment.NewLine + "扫描结束!" + Environment.NewLine);
            textBox1.ReadOnly = false;
            textBox2.ReadOnly = false;
            textBox3.ReadOnly = false;
        }

        private void Scan()
        {
            int portnow = port;
            //创建线程变量
            Thread Threadnow = scanThread;
            //扫描端口,成功则写入信息
            done[portnow] = true;
            //创建TcpClient对象,TcpClient用于为TCP网络服务提供客户端连接
            TcpClient objTCP = null;
            try
            {
                //用于TcpClient对象扫描端口
                objTCP = new TcpClient(hostAddress, portnow);
                //扫描到则显示到显示框
                textBox4.AppendText("端口 " + port + " 开放!" + Environment.NewLine);
            }
            catch
            {
                //未扫描到,则会抛出错误
            }
        }
    }
}

在这里插入图片描述
相比于单线程,多线程快很多

四、总结

本实验主要是用C#编写一个命令行/控制台的简单hello world程序,实现如下功能:在屏幕上连续输出50行“hello cqjtu!重交物联2019级”;同时打开一个网络UDP 套接字,向另一台室友电脑发送这50行消息。
用VS2019 的C#编写一个简单的Form窗口程序,有一个文本框 textEdit和一个发送按钮button,运行程序后,可以在文本框里输入文字,如“hello cqjtu!重交物联2019级”,点击button,将这些文字发送给室友电脑,采用UDP套接字;
编写端口扫描器程序,分别采用单一进程和多线程方式,对比两者的效果

五、参考链接

https://blog.csdn.net/ssj925319/article/details/109336123

https://blog.csdn.net/tyro_00/article/details/109827571

https://blog.csdn.net/qq_55691662/article/details/121455003?spm=1001.2014.3001.5501

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值