【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

 

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