zoukankan      html  css  js  c++  java
  • 使用.NetCore在Linux上写TCP listen 重启后无法绑定地址

    拥抱.net core的过程中, 将公司的一套java项目改成了.net core 2.0版的.

    里面的tcp服务被我用msdn的SocketAsyncEventArgs方式重写了, 然而在测试的过程中发现, 偶尔会出现重启无法再次绑定监听的情况.

    因为缺乏linux上编程的经验, 对linux的认识过于粗浅, 仅凭现有的知识第一反应是, 是不是在asp.net core的结束时没有清理干净, 也不是呀, 在lifetime中记录了日志都清楚地打印了.

    打开搜索引擎, 搜linux下socket绑定失败, 找到一条似乎有用的答案, socket options设置reuse address为true.

    有道理, 于是在绑定前加了一条: 

    listener.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.ReuseAddress, true);

    并没有什么软用...

    于是打算借鉴下开源项目中的做法. 翻了下supersocket, 发现尚未有支持netcore版, 于是跳过. 又翻了下dotnetty, 发现开始监听和结束两处都没什么区别. 

    突然想到asp.net core本身不就是能正常重启监听吗? 那看源码吧, 对了是Kestrel的源码, 于是GitHub, download, 简单搜索之后看到如下函数:

            [DllImport("libc", SetLastError = true)]
            private static extern int setsockopt(int socket, int level, int option_name, IntPtr option_value, uint option_len);
    
            private const int SOL_SOCKET_OSX = 0xffff;
            private const int SO_REUSEADDR_OSX = 0x0004;
            private const int SOL_SOCKET_LINUX = 0x0001;
            private const int SO_REUSEADDR_LINUX = 0x0002;
    
            // Without setting SO_REUSEADDR on macOS and Linux, binding to a recently used endpoint can fail.
            // https://github.com/dotnet/corefx/issues/24562
            private unsafe void EnableRebinding(Socket listenSocket)
            {
                var optionValue = 1;
                var setsockoptStatus = 0;
    
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    setsockoptStatus = setsockopt(listenSocket.Handle.ToInt32(), SOL_SOCKET_LINUX, SO_REUSEADDR_LINUX,
                                                  (IntPtr)(&optionValue), sizeof(int));
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    setsockoptStatus = setsockopt(listenSocket.Handle.ToInt32(), SOL_SOCKET_OSX, SO_REUSEADDR_OSX,
                                                  (IntPtr)(&optionValue), sizeof(int));
                }
    
                if (setsockoptStatus != 0)
                {
                    _trace.LogInformation("Setting SO_REUSEADDR failed with errno '{errno}'.", Marshal.GetLastWin32Error());
                }
            }

    真相大白. socket option 设置reuse address即可.

    然后解释上面代码的注释里也给出了, 在这里:

    https://github.com/dotnet/corefx/issues/24562

  • 相关阅读:
    QTP的那些事不能识别web上的测试对象
    ASP.NET 2.0 页面事件执行顺序 摘自http://www.cnblogs.com/chinadragon/archive/2009/11/21/1607761.html
    javascript replace方法的使用注意点
    HTML转义字符转载http://www.8189090.com/character/
    有无Global.asax文件对Session的影响
    四叉树索引引用自http://hi.baidu.com/geochenyj/blog/item/189f2fed07d041d6b31cb1b6.html
    GirdView固定行头
    CSS的常用滤镜(filter)属性及语句大全摘自http://www.8tops.com/113_skill_8267CEB6473B4AF1ABF669340E3AD2EF.htm
    Clone基类http://www.legalsoft.com.cn/docs/986.html
    自定义控件不能设置属性
  • 原文地址:https://www.cnblogs.com/pasoraku/p/8706825.html
Copyright © 2011-2022 走看看