使用.NetCore在Linux上寫TCP listen 重啓後沒法綁定地址

擁抱.net core的過程當中, 將公司的一套java項目改爲了.net core 2.0版的.java

裏面的tcp服務被我用msdn的SocketAsyncEventArgs方式重寫了, 然而在測試的過程當中發現, 偶爾會出現重啓沒法再次綁定監聽的狀況.linux

由於缺少linux上編程的經驗, 對linux的認識過於粗淺, 僅憑現有的知識第一反應是, 是否是在asp.net core的結束時沒有清理乾淨, 也不是呀, 在lifetime中記錄了日誌都清楚地打印了.git

打開搜索引擎, 搜linux下socket綁定失敗, 找到一條彷佛有用的答案, socket options設置reuse address爲true.github

有道理, 因而在綁定前加了一條: 編程

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

並無什麼軟用...asp.net

因而打算借鑑下開源項目中的作法. 翻了下supersocket, 發現還沒有有支持netcore版, 因而跳過. 又翻了下dotnetty, 發現開始監聽和結束兩處都沒什麼區別. socket

忽然想到asp.net core自己不就是能正常重啓監聽嗎? 那看源碼吧, 對了是Kestrel的源碼, 因而GitHub, download, 簡單搜索以後看到以下函數:tcp

        [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

相關文章
相關標籤/搜索