擁抱.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