【C#】TCP、UDP网络通讯使用汇总

一、TCP

1、一些概念

(1)帧的最小长度为64B, 最大长度为1518B。

(2)数据字段的最小长度为46B, 数据字段的最大长度为1500B。

(3)不同MTU下的数据包分片

使用TCP通信的源设备在网络上发送一个IP数据包,该数据包大小必须小于目标和中间网络的MTU。这个限制是由网络数据链路层和硬件MTU决定的。

如果数据包大于中间网络或目标设备的MTU,该数据包将进一步分成两半,这个过程称为分片。

 

IPv4:当不分片(DF)标志处于活动状态时,无法对数据包进行分片。

如果DF状态处于非活动状态,路由器可以将一个数据包分割成多个片段。目标设备稍后可以重新连接这些片段。它将返回未分片的数据包到源网络。

 参考:https://blog.csdn.net/usstmiracle/article/details/132228616

 

2、实例

(1)通过Socket读取大量数据

https://www.cnblogs.com/seekdream/p/6293286.html

(2)简单聊天程序

https://www.sohu.com/a/347125475_120050810

(3)解决连接超时时间过长问题

using (TcpClient tcp = new TcpClient())  
{  
    IAsyncResult ar = tcp.BeginConnect("127.0.0.1", 80, null, null);  
    System.Threading.WaitHandle wh = ar.AsyncWaitHandle;  
    try 
    {  
       if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5), false))  
       {  
           tcp.Close();  
           throw new TimeoutException();  
       }  
 
        tcp.EndConnect(ar);  
    }  
    finally 
    {  
        wh.Close();  
    }  
} 

参考链接

3、编写高性能Tcp Socket应用注意事项

https://www.cnblogs.com/smark/archive/2011/11/14/2248197.html

 

二、UDP

1、理论

https://zhuanlan.zhihu.com/p/439525960

 

2、实例

(1)使用UDP进行网络通信

https://blog.csdn.net/weixin_48822802/article/details/128454633

 (2)分包发送

https://blog.csdn.net/awangdea99/article/details/102960340

 

三、IP使用

1、用默认浏览器打开网址/IP

System.Diagnostics.Process.Start("https://11.12.123.123"); 

 https://blog.csdn.net/Lsc_hei/article/details/110529039

 

2、获取本地连接IP地址、无线连接IP地址

获取本机本地ip地址:

        public static IPAddress GetServerIP()
        {
            IPAddress ipaddress = IPAddress.Parse("0.0.0.0");
            NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface ni in interfaces)
            {
                if (ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
                {
                    foreach (UnicastIPAddressInformation ip in
                        ni.GetIPProperties().UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            ipaddress = ip.Address;
                        }
                    }
                }
            }
            return ipaddress;
        }

获取本地连接IP地址、无线连接IP地址:

        /// <summary>
        /// 获取本地连接IP地址、无线连接IP地址
        /// </summary>
        /// <param name="k">0:本地连接,1:本地连接1,2:无线网络连接</param>
        /// <returns></returns>
        public static IPAddress GetIP(int k)
        {
            NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
            int len = interfaces.Length;
            IPAddress[] mip = { IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0") };

            for (int i = 0; i < len; i++)
            {
                NetworkInterface ni = interfaces[i];

                if (ni.Name == "本地连接")
                {
                    IPInterfaceProperties property = ni.GetIPProperties();
                    foreach (UnicastIPAddressInformation ip in property.UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            mip[0] = ip.Address;
                        }
                    }
                }
                else if (ni.Name == "本地连接2")
                {
                    IPInterfaceProperties property = ni.GetIPProperties();
                    foreach (UnicastIPAddressInformation ip in property.UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            mip[1] = ip.Address;
                        }
                    }
                }
                else if (ni.Name == "无线网络连接")
                {
                    IPInterfaceProperties property = ni.GetIPProperties();
                    foreach (UnicastIPAddressInformation ip in property.UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            mip[2] = ip.Address;
                        }
                    }
                }
            }
            return mip[k];
        }

链接:https://www.jianshu.com/p/be7fa27d9104

 

3、判断一个端口是不是被占用以及返回一个空闲端口

    public static class IPAndPortHelper
    {
        #region 成员字段

        /// <summary>
        /// 同步锁
        /// 用来在获得端口的时候同步两个线程
        /// </summary>
        private static object inner_asyncObject = new object();

        /// <summary>
        /// 开始的端口号
        /// </summary>
        private static int inner_startPort = 50001;

        #endregion

        #region 获得本机所使用的端口

        /// <summary>
        /// 使用 IPGlobalProperties 对象获得本机使用的端口
        /// </summary>
        /// <returns>本机使用的端口列表</returns>
        private static List<int> GetPortIsInOccupiedState()
        {
            List<int> retList = new List<int>();
            //遍历所有使用的端口,是不是与当前的端口有匹配
            try
            {
                //获取本地计算机的网络连接和通信统计数据的信息
                IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
                //返回本地计算机上的所有Tcp监听程序
                IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
                //返回本地计算机上的所有UDP监听程序
                IPEndPoint[] ipsUDP = ipProperties.GetActiveUdpListeners();
                //返回本地计算机上的Internet协议版本4(IPV4 传输控制协议(TCP)连接的信息
                TcpConnectionInformation[] tcpConnInfoArray = ipProperties.GetActiveTcpConnections();

                //将使用的端口加入
                retList.AddRange(ipEndPoints.Select(m => m.Port));
                retList.AddRange(ipsUDP.Select(m => m.Port));
                retList.AddRange(tcpConnInfoArray.Select(m => m.LocalEndPoint.Port));
                retList.Distinct();//去重
            }
            catch (Exception ex)//直接抛出异常
            {
                throw ex;
            }

            return retList;
        }

        /// <summary>
        /// 使用 NetStat 命令获得端口的字符串
        /// </summary>
        /// <returns>端口的字符串</returns>
        private static string GetPortIsInOccupiedStateByNetStat()
        {
            string output = string.Empty;
            try
            {
                using (Process process = new Process())
                {
                    process.StartInfo = new ProcessStartInfo("netstat", "-an");
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.Start();
                    output = process.StandardOutput.ReadToEnd().ToLower();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return output;
        }

        #endregion

        #region 获得一个当前没有被使用过的端口号

        /// <summary>
        /// 获得一个当前没有被使用过的端口号
        /// </summary>
        /// <returns>当前没有被使用过的端口号</returns>
        public static int GetUnusedPort()
        {
            /*
             * 在端口获取的时候防止两个进程同时获得一个一样的端口号
             * 在一个线程获得一个端口号的时候,下一个线程获取会从上一个线程获取的端口号+1开始查询
             */
            lock (inner_asyncObject)//线程安全
            {
                List<int> portList = GetPortIsInOccupiedState();
                string portString = GetPortIsInOccupiedStateByNetStat();

                for (int i = inner_startPort; i < 60000; i++)
                {
                    if (portString.IndexOf(":" + inner_startPort) < 0 &&
                        !portList.Contains(inner_startPort))
                    {
                        //记录一下 下次的端口查询从 inner_startPort+1 开始
                        inner_startPort = i + 1;
                        return i;
                    }
                }

                //如果获取不到
                return -1;
            }
        }

        #endregion
    }

来源:https://www.cnblogs.com/Jeffrey-Chou/p/12375743.html

 

 

四、问题汇总

1、UDP通讯代码没问题但收不到其他地址发来的数据

尝试关闭防火墙。

https://bbs.csdn.net/topics/396417937

2、端口一直被占用

首先,可以使用netstat命令查找和监控服务器上的UDP端口占用情况,该命令会列出服务器上所有正在使用的UDP端口及其相应的端口号、进程ID和状态。根据进程ID可以确定占用该端口的进程

netstat -ano | findstr "UDP"

接下来,可以使用Task Manager或者命令行中的Taskkill命令来结束占用UDP端口的进程。这个命令会强制终止指定进程ID的进程。请注意,在使用这种方法之前,请确保你已经了解并且确认将要终止的进程确实是不必要的或者是不希望占用UDP端口的。

taskkill /F /PID 进程ID

如果你无法确定具体的进程ID,也可以使用端口号进行终止。

taskkill /F /PID 进程号

进程号是占用UDP端口的进程在Task Manager中显示的进程标识号。
另外,如果你确定占用UDP端口的进程是你自己的应用程序,你可以在代码中关闭UDP连接并释放端口。
除此之外,你还可以通过修改服务器的防火墙设置来阻止占用UDP端口的进程访问网络。具体的步骤会因不同的操作系统和防火墙而有所不同,你可以参考操作系统和防火墙的官方文档或者网上的教程进行设置。
需要注意的是,关闭占用UDP端口的进程可能会导致相关的应用程序或服务无法正常工作,所以在进行操作之前请确保了解其影响并谨慎操作。同时,遵循服务器管理的最佳实践,确保服务器的安全和稳定性。

来源:https://worktile.com/kb/ask/1147096.html

 

posted @ 2021-07-31 11:35  不溯流光  阅读(97)  评论(0编辑  收藏  举报