TCP/UDP编程基础

本文档详细介绍了使用C#进行UDP通信的步骤,包括创建控制台应用发送和接收数据,以及在Form窗口程序中实现交互。同时,对比分析了单线程和多线程端口扫描的效率,并展示了抓包分析的过程,揭示了IP数据包的关键字段信息。整个实验展示了网络编程的基础知识和实践技巧。
摘要由CSDN通过智能技术生成

一、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发送给另一台电脑相关信息,以及通过可视化窗口的形式再次实现上述要求,并编写端口扫描程序,再进行抓包,总体来说还是有一定难度,在网上找了不少的参考资料,收获较多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值