TCP/UDP编程基础

本文介绍了C#中UDP通信的基础实现,包括VS项目创建、发送与接收代码示例,以及Form窗口程序的设计。后续探讨了多线程端口扫描技术,并对比了单线程效果。此外,文章还涉及了UDP包解析和与TCP的差异,总结了TCP/UDP编程要点。
摘要由CSDN通过智能技术生成

一、UDP通信

1.vs创建项目

在这里插入图片描述
选择创建新项目,选择控制台应用
在这里插入图片描述
接下来选择路径完成创建

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("169.254.184.236");  //假设发送给这个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.界面设计

从工具箱中选择button和textbox控件
在这里插入图片描述
textbox选择多行文本
在这里插入图片描述
添加垂直滚动条:找到 ScrollBars 属性,设置参数为 Vertical
在这里插入图片描述
设置边界样式:找到 BorderStyle ,参数设置为 FixedSingle
在这里插入图片描述
设置消息显示界面的 TextBox 不可编辑:找到 Enabled 属性,参数设为 False
在这里插入图片描述
设置按钮
在这里插入图片描述
设置窗体
窗体text设置为客户端
找到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();
        }

        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";IP地址
                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 = "";
        }
    }
}
服务器端代码
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);
                }
            }
        }
    }
}

5.结果

在这里插入图片描述
在这里插入图片描述

三、端口扫描

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.结果对比

单线程操作的时候会出现界面直接卡死并且扫描速度很慢,
多线程操作扫描速度大大提升而且不会出现界面卡死

四、抓包分析

UDP过滤
在这里插入图片描述
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窗口程序
UDP与TCP包的区别
UDP:
在这里插入图片描述
第三行:Internet Protocl Version 4:IPv4协议
第四行:User Datagram Protocol:UDP协议
TCP:
在这里插入图片描述
第三行:Internet Protocl Version 4:IPv4协议
第四行:Transmission Control Protocol:TCP协议

五、总结

TCP 创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议。
UDP创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样,但是不需要调用listen()方法,而是直接接收来自任何客户端的数据。
在TCP编程里面,accept()方法返回客户端的socket 和 地址及端口, recv()方法用于接收对方发送过来的数据,send() 方法用于向对方发送数据。
在UDP编程里面,recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。
TCP and UDP 里面编程时发送与接收数据时要做encode() 编码与decode() 解码处理。

六、参考

1.TCP & UDP 总结
2.C#使用TCP/UDP协议通信并用Wireshark抓包分析数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ivan@Xiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值