我想在TCP服務端中主動斷開與客戶端的Socket連接,網上查了一些檔案,設定了Socket的SO_LINGER選項
(檔案是這么描述的:l_onoff不為0(開啟),l_linger等于0:此時close系統呼叫立即回傳,TCP模塊將丟棄被關閉的socket對應的TCP發送緩沖區中殘留的資料,同時給對方發送一個復位報文段(RST)。因此,這種情況給服務器提供了例外終止一個連接的方法。)
我按照檔案在服務端進行了SO_LINGER設定,但我用wireshark使用看不到服務端發出的RST包,在服務端主動Close Socket后,客戶端連接狀態是Close_Wait,服務端為FIN_WAIT_2, wrieshark中只能看到兩次斷聯揮手,只有當客戶端再次發送資料時才能看見RST包
請問,是為設定的問題還是別的什么原因啊?

服務端代碼:
namespace ServerWithBeat
{
class Program
{
const string IP = "192.168.1.22";
static void Main(string[] args)
{
Console.Title = "服務端啟用Keep-alive機制";
MyTcpListener _tcpServer = new MyTcpListener(IPAddress.Parse(IP), 44818);
_tcpServer.Start();
Console.WriteLine("服務端啟動,開始監聽44818埠...");
Task _tListening = new Task(() =>
{
try
{
while (_tcpServer.Active)
{
TcpClient _tcpcClient = _tcpServer.AcceptTcpClient();
Console.WriteLine("客戶端{0}:{1}已經接入。", (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Address, (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Port);
//接入一個客戶端后,單獨為其開啟一個執行緒使服務端和客戶端通信
Task.Run(() =>
{
#region 服務端設定Keep-alive心跳功能
_tcpcClient.Client.IOControl(IOControlCode.KeepAliveValues, GetKeepAliveData(), null);//設定Keep-alive引數
_tcpcClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);//啟用Keep-alive
#endregion
#region 服務端Socket設定ReceiveTimeout選項來控制接受超時時間
_tcpcClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 60000);//設定60秒未接受到資料視為超時,超時后_stream.Read將例外,進入catch后執行close關閉socket
#endregion
#region 服務端socket設定SO_LINGER啟用并設定超時時間為0,強制Close后發送RST包
//以下兩種設定,都沒有測驗通過:服務端Close掉Socket后,wireshark都沒有抓到RST包,只有在客戶端再次Send時才看到了RST包
//_tcpcClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
//_tcpcClient.Client.LingerState = new LingerOption(true, 0);
_tcpcClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
#endregion
NetworkStream _stream = _tcpcClient.GetStream();
try
{
byte[] _data = Encoding.Default.GetBytes("歡迎,我是服務端!");
_stream.Write(_data, 0, _data.Length);
while ((_tcpServer.Active) && (_tcpcClient.Connected))
{
_data = new byte[1024];
int _len = _stream.Read(_data, 0, 1024);
if (_len == 0)
break;
Console.WriteLine("來自客戶端{0}:{1}:{2}", (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Address, (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Port, Encoding.Default.GetString(_data, 0, _len));
if ("killme" == Encoding.Default.GetString(_data, 0, _len))
break;
}
Console.WriteLine("客戶端{0}:{1}斷開連接。", (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Address, (_tcpcClient.Client.RemoteEndPoint as IPEndPoint).Port);
_stream.Close();
_tcpcClient.Close();
}
catch(Exception ex)
{
Console.WriteLine("客戶端連接例外:" + ex.Message);
try
{
_stream.Close();
_tcpcClient.Close();
}
catch { }
}
});
}
}
catch(Exception ex)
{
Console.WriteLine("服務端連接例外:" + ex.Message);
}
});
_tListening.Start();
Console.ReadKey();
_tcpServer.Stop();
_tListening.Wait();
//Task.WaitAll();
Console.WriteLine("服務端停止,按任意鍵關閉程式。");
Console.ReadKey();
}
static byte[] GetKeepAliveData()
{
uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
//啟用KeepaliveValue功能
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
//連接空閑5秒后開始發送心跳包
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)500).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
return inOptionValues;
}
}
//定義一個TcpListener子類,開放出TcpListener protected的Active屬性
class MyTcpListener : TcpListener
{
public MyTcpListener(IPEndPoint localEP) : base(localEP)
{
}
public MyTcpListener(int port) : base(port)
{
}
public MyTcpListener(IPAddress localaddr, int port) : base(localaddr, port)
{
}
public new bool Active
{
get
{
return base.Active;
}
}
}
客戶端代碼:
namespace ClientNoBeat
{
class Program
{
const string IP = "192.168.1.22";
static void Main(string[] args)
{
Console.Title = "客戶端沒有啟用Keep-alive機制";
TcpClient _tcpClient = new TcpClient(IP, 44818);
Console.WriteLine("已連接到服務器192.168.1.22:44818,本地埠" + (_tcpClient.Client.LocalEndPoint as IPEndPoint).Address + ":" + (_tcpClient.Client.LocalEndPoint as IPEndPoint).Port);
NetworkStream _stream = _tcpClient.GetStream();
byte[] _buff = new byte[1024];
int _len = _stream.Read(_buff, 0, 1024);
Console.WriteLine(Encoding.Default.GetString(_buff, 0, _len));
while (_tcpClient.Connected)
{
string _msg = Console.ReadLine();
if ("exit" == _msg)
break;
byte[] _data = Encoding.Default.GetBytes(_msg);
_stream.Write(_data, 0, _data.Length);
}
_stream.Close();
_tcpClient.Close();
Console.WriteLine("與服務端連接已斷開,按任意鍵結束程式。");
Console.ReadKey();
}
}
}
uj5u.com熱心網友回復:
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/185132.html
上一篇:C# 如何選擇匯總GridControl控制元件的任意底部匯總資料?
下一篇:如何關閉FileStrem?
