I need your help guys in order to fix issue with non closing TCP connection.
Basically it works fine but after a few minutes it stucks with connection in CLOSE_WAIT state.
The logic of the code:
The code accepts first packet and parse it and after that it sends the CRC back to the client. If the CRC valid then client sends the main packet to the server and repeat it. Once if there are no packets then client close connection but sometimes it does not. In this case server (code below) close connection after 1 minute of the communications.
I assume it should correspond to https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/TCP-states-explained/ta-p/78462 as well
Here is C# code
void TCPListenerServer(object obj) {
CancellationToken token = (CancellationToken) obj;
if (token.IsCancellationRequested) {
isDisposed = true;
if (listener != null) {
listener.Stop();
}
return;
} else {
try {
isDisposed = false;
try {
var validIP = IPAddress.Parse(Properties.Settings.Default.ServerIP);
listener = new TcpListener(validIP, Properties.Settings.Default.ServerPort);
listener.Start();
while (isDisposed == false || token.IsCancellationRequested == false) {
if (token.IsCancellationRequested || isDisposed) {
break;
} else {
if (!listener.Pending()) {
Thread.Sleep(50);
continue;
}
listener.Server.ReceiveTimeout = 10000;
listener.Server.LingerState = new LingerOption(true, 0);
var client = listener.AcceptTcpClient();
var arrThread = new ThreadParams() {
Client = client, Token = m_Cts.Token
};
var t = new Thread(ProcessClientRequests) {
IsBackground = true,
Name = "ClientConnectionThread",
};
clientThreads.Add(t);
t.Start((object) arrThread);
}
}
} catch (SocketException ex) {
if (ex.SocketErrorCode == SocketError.Interrupted) {}
} catch (Exception ex) {} finally {
if (listener != null) {
listener.Server.Close();
listener.Stop();
}
}
} catch (Exception ex) {
}
}
}
private void ProcessClientRequests(object argument) {
TcpClient client = ((ThreadParams) argument).Client;
CancellationToken token = ((ThreadParams) argument).Token;
client.SendTimeout = 10000;
client.ReceiveTimeout = 10000;
client.LingerState = new LingerOption(true, 0);
var bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
var isFirstPacket = true;
var startTime = DateTime.Now;
DateTime endTime = DateTime.Now;
try {
using(NetworkStream stream = client.GetStream()) {
do {
Thread.Sleep(20);
} while (!stream.DataAvailable);
while ((client != null && client.Connected) && stream != null && stream.CanRead && (endTime - startTime).TotalMinutes < 1) {
if (client == null) {
break;
}
do {
if (token.IsCancellationRequested) {
return;
}
if (client == null) {
break;
}
endTime = DateTime.Now;
int streamReadBytes = 0;
streamReadBytes = stream.Read(buffer, 0, buffer.Length);
if (streamReadBytes == 0) {
if (client != null) {
client.Close();
}
break;
}
if (buffer[0] == (byte) GalileoskyPacketHeaderEnums.FirstPacket || buffer[0] == (byte) GalileoskyPacketHeaderEnums.MainPacket) {
var parserGalileosky = new Galileosky();
var packetResult = parserGalileosky.ParsePacket(buffer, isFirstPacket);
if (packetResult == null) {
if (client != null) {
client.Close();
client = null;
}
break;
}
if (packetResult.Errors.Any()) {
if (client != null) {
client.Close();
client = null;
}
} else {
var imei = packetResult.Packet.IMEI;
if (isFirstPacket) {
isFirstPacket = false;
if (stream.CanWrite == true && packetResult.Packet.IsCrc) {
var answerPacket = packetResult.Packet.GetConfirmPacket();
stream.Write(answerPacket.Ready);
} else {
if (client != null) {
client.Close();
client = null;
}
}
} else // The Main Packet processing
{
// ... Some code to send the main packet to the app queue
if (stream.CanWrite == true && !packetResult.Errors.Any() && packetResult.Packet.IsCrc) {
var answerPacket = packetResult.Packet.GetConfirmPacket();
stream.Write(answerPacket.Ready);
}
if (packetResult.Packet.IsExtraData == false) {
if (client != null) {
client.Close();
client = null;
break;
}
}
}
}
} else {
if (client != null) {
client.Close();
client = null;
}
}
if ((endTime - startTime).TotalMinutes > 1) {
if (client != null) {
client.Close();
client = null;
break;
}
}
}
while ((client != null && client.Connected) && stream != null && stream.CanRead && stream.DataAvailable && (endTime - startTime).TotalMinutes < 1);
}
}
if (client != null) {
client.Close();
client = null;
}
} catch (Exception ex) {} finally {
if (client != null) {
client.Close();
client = null;
}
}
}
Even if your code is correct, you can hit the "2 generals" problem. There is no perfect algorithm for two parties to agree that a connection is closed, when there may be packet loss.
Gracefully closing a TCP stream requires both parties to shutdown sending, read data up to EOF, then close. I believe that you'll see a socket in CLOSE_WAIT, if one party has shutdown the connection, but the other has not. It looks like you have set your sockets to linger. In which case the operating system will take over the lifetime of the socket for you.
If your socket is not set to linger, then closing the socket early will cause data that you think you've sent to be lost.
I should also point out that your code is drastically over complicated, with error handling repeated everywhere. It looks like it was probably written before C# introduced async / await.
It also seems that you are assuming that one read operation equates to one data packet. But this is a TCP stream. The OS is allowed to fragment and recombine data that was written from one end, into any number of data reads at the other.
I'd suggest you search the internet for an example that uses .AcceptTcpClientAsync, .ReadAsync, .WriteAsync etc.
Finally I could'n find a way to solve problem to close connection within my code.
But I added a timer that close connection and it works just awesome!
private void ProcessClientRequests(object argument)
// ... The same code of my quetion
Timer timerCloseConn = new(new TimerCallback((e) =>
{
if (stream != null)
{
stream.Close();
stream.Dispose();
}
if (client != null)
{
if (client.Client.Connected)
{
client.Client.Shutdown(SocketShutdown.Both);
}
client.Close();
client.Dispose();
client = null;
}
Logger.Info("The connection has been closed by
timer's rule!");
}), null, 60000, Timeout.Infinite);
// ... The same code of my quetion
}
Related
I am currently developing a one-to-many relationship between one server and many clients.
Everytime a client connects I append to the client list: clientList.Add(client);
I wrote the following code to check the "pulse" of the client connection as to see if the client is still connected and currently updating the toolStripStatusLabel1
This seems to work well when checking one connection and in addition, I added the exception cleanup() when trying to send data so that it should check at all scenarios(Feel free to give your opinion).
My question now is, how do I check the connection from multiple clients?
Please find below the reference code:
private void StartListen()
{
//Creating a TCP Connection and listening to the port
tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666);
tcpListener.Start();
toolStripStatusLabel1.Text = "Listening on port 6666 ...";
int counter = 0;
appStatus = 0;
while (true)
{
try
{
client = tcpListener.AcceptTcpClient();
counter++;
clientList.Add(client);
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
//Updating status of connection
toolStripStatusLabel1.Text = "Connected from " + IPAddress.Parse(ipend.Address.ToString());
appStatus = 1;
th_outPutStream = new Thread(delegate () { outPutStream(client); });
th_outPutStream.Start();
th_inPutStream = new Thread(delegate () { inPutStream(client); });
th_inPutStream.Start();
th_checkConnection = new Thread(checkConnection);
th_checkConnection.Start();
}
catch (Exception err)
{
Cleanup();
}
}
}
private void checkConnection()
{
bool status = true;
while (status == true)
{
status = IsConnected();
if (status == true)
{
System.Threading.Thread.Sleep(3000); //Wait 3 seconds then try again
}
else
{
Cleanup();
}
}
}
private bool IsConnected()
{
try
{
return !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0);
}
catch (SocketException) { Cleanup(); return false; }
}
I did this by simply creating a forloop for each client in my client list:
private void checkConnection()
{
bool status = true;
while (true)
{
for (int i = 0; i < clientList.Count; i++)
{
Debug.WriteLine(clientList.Count);
status = IsConnected(i);
if (status == true)
{
}
else
{
Cleanup(i);
}
}
System.Threading.Thread.Sleep(3000); //Wait 3 seconds then try again
}
}
private bool IsConnected(int i)
{
try
{
return !(clientList[i].Client.Poll(1, SelectMode.SelectRead) && clientList[i].Client.Available == 0);
}
catch (SocketException) { Cleanup_dep(); return false; }
}
I am writing a packet sniffer using C# and PcapDotNet, I've successfully implemented the feature and I was able to capture all the TCP packets from my laptop, the problem is if I target my android device I get no packets at all,
I am a beginner at networking, so I think I am probably missing something.
This is my code for the packet handler, I am using an ObjectListView with a model object that contains all the information I want to list, also I have the network adapter in Promiscuous mode with a Berkeley filter to get only TCP packets with port numbers 443 and 80 that contains data (No SYN, FIN, ACK-only packets) from a specific Mac address
Code:
private void PacketHandler(PcapDotNet.Packets.Packet packet)
{
if (packet == null) { return; }
if (packet.Ethernet == null) { return; }
if (packet.Ethernet.IpV4 == null) { return; }
if (packet.Ethernet.IpV4.Tcp == null) { return; }
if (packet.Ethernet.IpV4.Tcp.Http == null) { return; }
var acpacket = new AcceptedPacket(); //Model Object
acpacket.Packet = packet;
try
{
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
if (packet.Ethernet.Source.ToString() == targetmac)
{
if (http.IsRequest && http.IsValid)
{
if (materialListView1.InvokeRequired)
{
materialListView1.BeginInvoke(new Action(() => {
materialListView1.AddObject(acpacket); }));
}
else
{
materialListView1.AddObject(acpacket);
}
ListofAcceptedPackets.Add(acpacket);
}
}
}
catch (Exception ex)
{
MetroMessageBox.Show(this, ex.Message, "Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
And this how I opened the adapter :
using (communicator =
selectedDevice.Open(65536,PacketDeviceOpenAttributes.Promiscuous,
1000))
{
if (communicator.DataLink.Kind !=
DataLinkKind.Ethernet)
{
if (MetroMessageBox.Show(this, "Only Ethernet is supported in this operation!","Error", MessageBoxButtons.OK,MessageBoxIcon.Error) == DialogResult.OK)
{
return;
}
}
using (BerkeleyPacketFilter filter = communicator.CreateFilter($"tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and ether src {targetmac.ToLower()} or tcp port 443 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and ether src {targetmac.ToLower()}"))
{
communicator.SetFilter(filter);
}
flag = true;
Packet packet;
do
{
PacketCommunicatorReceiveResult result =
communicator.ReceivePacket(out packet);
switch (result)
{
case
PacketCommunicatorReceiveResult.Timeout:
continue;
case
PacketCommunicatorReceiveResult.Ok:
{
PacketHandler(packet);
}
break;
default:
break;
}
} while (flag);
}
I also tried without any filter and couldn't reach the android device packets.
so is it possible to capture TCP packets from other devices on the network besides my own device? or maybe there's something wrong with my implementation?
Edit: after trying a bit more I was able to successfully get the TCP packets from the android device but only while implementing ARP Cache Poisoning at the same time because the device thinks that I am the gateway. so my question stands, can this be done without ARP Cache Poisoning because I think it's a bit aggressive for a packet sniffer.
I was finally able to make it work by applying ARP cache poisoning while the code below is able to redirect packets for any device to their destination, this way you get to capture packets for any device on the network specifically without losing internet access for this device.
Code:
private void StartSniffer()
{
RawCapture rawCapture;
do
{
if ((rawCapture = capturedevice.GetNextPacket()) != null)
{
EthernetPacket Packet = PacketDotNet.Packet.ParsePacket(rawCapture.LinkLayerType, rawCapture.Data) as EthernetPacket;
if (Packet == null) { return; }
AcceptedPacket acPacket = new AcceptedPacket();
acPacket.Packet = Packet;
if (Packet.SourceHwAddress.Equals(TargetMAC))
{
Packet.SourceHwAddress = capturedevice.MacAddress;
Packet.DestinationHwAddress = GatewayMAC;
capturedevice.SendPacket(Packet);
if (acPacket.TCPPacket != null &&
((acPacket.Type.Equals("HTTPS") && acPacket.TCPPacket.PayloadData != null) ||
(acPacket.Type.Equals("HTTP") && acPacket.TCPPacket.PayloadData != null)))
{
materialListView1.BeginInvoke(new Action(() =>
{
materialListView1.AddObject(acPacket);
if (materialListView1.Items.Count > 15 && !ResizeDone)
{
olvColumn8.MaximumWidth = 65;
olvColumn8.MinimumWidth = 65;
olvColumn8.Width = 65;
ResizeDone = true;
}
ListofAcceptedPackets.Add(acPacket);
}));
}
}
else if (Packet.SourceHwAddress.Equals(GatewayMAC))
{
IPv4Packet IPV4 = Packet.Extract(typeof(IPv4Packet)) as IPv4Packet;
if (IPV4.DestinationAddress.Equals(Target))
{
Packet.SourceHwAddress = capturedevice.MacAddress;
Packet.DestinationHwAddress = TargetMAC;
capturedevice.SendPacket(Packet);
}
if (Properties.Settings.Default.PacketDirection == "Inbound")
{
if (acPacket.TCPPacket != null &&
((acPacket.Type.Equals("HTTPS") && acPacket.TCPPacket.PayloadData != null) ||
(acPacket.Type.Equals("HTTP") && acPacket.TCPPacket.PayloadData != null)))
{
materialListView1.BeginInvoke(new Action(() =>
{
materialListView1.AddObject(acPacket);
if (materialListView1.Items.Count > 15 && !ResizeDone)
{
olvColumn8.MaximumWidth = 65;
olvColumn8.MinimumWidth = 65;
olvColumn8.Width = 65;
ResizeDone = true;
}
ListofAcceptedPackets.Add(acPacket);
}));
}
}
}
}
} while (snifferStarted);
And this is the capture device setup:
try
{
snifferStarted = true;
if (capturedevice != null)
{
capturedevice.Open(DeviceMode.Promiscuous, 1000);
capturedevice.Filter = $"(ip and ether src {targetmac.ToLower()}) or (ip and ether src {gatewayMAC.ToLower()} and dst net {Target})";
new Thread(() => { StartSniffer(); }).Start();
}
else
{
MetroMessageBox.Show(this, "No Capture Device is selected!", "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
catch (Exception exception)
{
MetroMessageBox.Show(this, exception.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
Note: this has been done using Packet.Net NOT PcapDotNet.
I am new to Sockets this is my Code to send and receive data from a server.
This Code Works fine as long as the Client is able to receive data from the server.
In Case the Server does not sends the answer back in the given time then the application needs to send "No Answer Received".
How can i know if the recvBuffer is empty or NULL.
Currently the if Condition for the recvBuffer does not work and the application tries to send empty buffer which results in "System.IndexOutOfRangeException error".
class GetSocket
{
public string SocketSendReceive(string server, int port, string cmd)
{
byte[] recvBuffer = new byte[1024];
TcpClient tcpClient = new TcpClient();
tcpClient.Client.ReceiveTimeout = 200;
try
{
tcpClient.Connect(server, 6100);
}
catch (SocketException e)
{
MessageBox.Show(e.Message);
}
if (tcpClient != null && tcpClient.Connected)
{
try
{
tcpClient.Client.Send(Encoding.UTF8.GetBytes(cmd));
tcpClient.Client.Receive(recvBuffer);
}
catch (SocketException e)
{
MessageBox.Show(e.ErrorCode.ToString());
}
tcpClient.GetStream().Close();
tcpClient.Client.Close();
tcpClient.Client.Dispose();
tcpClient = null;
string tmp = Encoding.ASCII.GetString(recvBuffer, 0, recvBuffer.Length);
if (recvBuffer != null && recvBuffer.Length > 0)
{
string[] words = tmp.Split(null);
return words[1];
}
else
{
return ("No Answer Received");
}
}
return null;
}
}
The Following code works fine after making changes as suggested.
class GetSocket
{
public string SocketSendReceive(string server, int port, string cmd)
{
byte[] recvBuffer = new byte[1024];
TcpClient tcpClient = new TcpClient();
tcpClient.Client.ReceiveTimeout = 200;
string tmp;
try
{
tcpClient.Connect(server, 6100);
}
catch (SocketException e)
{
MessageBox.Show(e.Message);
}
if (tcpClient != null && tcpClient.Connected)
{
try
{
tcpClient.Client.Send(Encoding.UTF8.GetBytes(cmd));
tcpClient.Client.Receive(recvBuffer);
tmp = Encoding.ASCII.GetString(recvBuffer, 0, recvBuffer.Length);
string[] words = tmp.Split(null);
return words[1];
}
catch (SocketException e)
{
return ("No Answer Received");
}
}
return null;
}
}
Using: Windows 10, C# .NET 2015 Community, UWP
Im trying to build a windows-universal-app that pairs my PC with a BLE device.
Whats already working is enumerating nearby devices, pair with a selected one and getting information like battery-level and firmware-revision.
The problem now is that when I try to get a custom service, my task ends because of an "System.Exception" at .GetGattService
System.Exception.Message: "Element not found. (Exception from HRESULT: 0x80070490)"
System.Exception.Stack : "at Windows.Devices.Bluetooth.BluetoothLEDevice.GetGattService(Guid serviceUuid)\r\n at SettingsCs.Settings.d__23.MoveNext()"
This is the code that's not working:
private async Task<SettingsReturn> writeSettingTransition(BluetoothLEDevice device, byte[] byteSettings)
{
//Check if device is available
if (device != null)
{
Guid SERVICE_CUSTOM = new Guid("7e0bc6be-8271-4f5a-a126-c24220e6250c");
GattDeviceService service = device.GetGattService(SERVICE_CUSTOM);
//Check if service is available
if (service == null)
{
return SettingsReturn.INIT_ERROR;
}
GattCharacteristic characteristic = service.GetCharacteristics(BLETestApp.CHAR_SETTINGS)[0];
//Check if characteristic is available
if (characteristic == null)
{
return SettingsReturn.INIT_ERROR;
}
var writer = new DataWriter();
writer.WriteBytes(byteSettings);
var buffer = writer.DetachBuffer();
await characteristic.WriteValueAsync(buffer);//********
bool success = characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Write);
if (success == true)
{
// Take care of the 8 bit byte for the counter (max = 255 (unsigned))
if (TRANSACTION_ID > 250)
{
TRANSACTION_ID = 0;
}
else
{
// Count TANSACTION_ID one up
TRANSACTION_ID++;
}
return SettingsReturn.OK;
}
else
{
return SettingsReturn.WRITE_ERROR;
}
}
else
{
return SettingsReturn.INIT_ERROR;
}
}
I hope somenone can help me or tell me what I'm doing wrong.
I can't pinpoint the error you get, use the debugger to check if your characteristic has write permission.
I have changed your code in a way I write to my device successfully.
Also added a try catch block.
Here it is:
private async Task<SettingsReturn> writeSettingTransition(BluetoothLEDevice device, byte[] byteSettings)
{
bool success = false;
//Check if device is available
if (device != null)
{
Guid SERVICE_CUSTOM = new Guid("7e0bc6be-8271-4f5a-a126-c24220e6250c");
GattDeviceService service = device.GetGattService(SERVICE_CUSTOM);
//Check if service is available
if (service == null)
{
return SettingsReturn.INIT_ERROR;
}
GattCharacteristic characteristic = service.GetCharacteristics(BLETestApp.CHAR_SETTINGS)[0];
//Check if characteristic is available
if (characteristic == null)
{
return SettingsReturn.INIT_ERROR;
}
IBuffer writeBuffer = byteSettings.AsBuffer();// using Windows.Storage.Streams
try
{
// BT_Code: Writes the value from the buffer to the characteristic.
var result = await characteristic.WriteValueAsync(writeBuffer);
if (result == GattCommunicationStatus.Success)
{
// NotifyUser("Successfully wrote value to device" );
success = true;
}
else
{
// NotifyUser($"Write failed: {result}");
success = false;
}
}
catch (Exception ex) when ((uint)ex.HResult == 0x80650003 || (uint)ex.HResult == 0x80070005)
{
// E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED or E_ACCESSDENIED
// This usually happens when a device reports that it support writing, but it actually doesn't.
// NotifyUser(ex.Message, NotifyType.ErrorMessage);
}
if (success)
{
// Take care of the 8 bit byte for the counter (max = 255 (unsigned))
if (TRANSACTION_ID > 250)
{
TRANSACTION_ID = 0;
}
else
{
// Count TANSACTION_ID one up
TRANSACTION_ID++;
}
return SettingsReturn.OK;
}
else
{
return SettingsReturn.WRITE_ERROR;
}
}
else
{
return SettingsReturn.INIT_ERROR;
}
}
There can be typos and other mishaps, hope it helps.
On server side I have this code which running in new thread
static void ListenForConsultant()
{
while (true)
{
var serverSocket = new TcpListener(IPAddress.Any, 2111);
serverSocket.Start();
var clientSocket = serverSocket.AcceptTcpClient();
consultantConnected = true;
Console.WriteLine(" >> Consultant Connected");
byte[] bytesFrom = new byte[10025];
while (true)
{
if (!clientSocket.Connected)
{
break;
}
NetworkStream networkStream = clientSocket.GetStream();
bytesFrom = new byte[10025];
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
var dataFromConsultant = System.Text.Encoding.ASCII.GetString(bytesFrom);
if (dataFromConsultant.IndexOf("~") != -1 && dataFromConsultant.IndexOf("^") != -1 && dataFromConsultant.IndexOf("^") > dataFromConsultant.IndexOf("~"))
{
var lengthOfMessage = dataFromConsultant.IndexOf("^") - dataFromConsultant.IndexOf("~") - 1;
dataFromConsultant = dataFromConsultant.Substring(dataFromConsultant.IndexOf("~") + 1, lengthOfMessage);
Console.WriteLine(" >> From consultant:" + dataFromConsultant);
}
}
consultantConnected = false;
Console.WriteLine(" >> Consultant Disconnected");
serverSocket.Stop();
}
}
I connect using putty to port 2111. All works ok, but when I close putty socket doesn't close, however I have condition
if (!clientSocket.Connected)
{
break;
}
Debug shows me that clientSocket.Connected is true even after I disconnected from server.
Why does this happen?
The tcpClient.Connected property value is not reliable, it's value depending on the last communication; so if the last communication were succeed then it's value is true otherwise it is false. for more information on that check this.
Use this IsConnected property to check if the tcpClient is connected:
public static bool IsConnected
{
get
{
try
{
//return _tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected;
if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
{
/* As the documentation:
* When passing SelectMode.SelectRead as a parameter to the Poll method it will return
* -either- true if Socket.Listen(Int32) has been called and a connection is pending;
* -or- true if data is available for reading;
* -or- true if the connection has been closed, reset, or terminated;
* otherwise, returns false
*/
// Detect if client disconnected
if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Edit: Note that checking IsConnected will not prevents it from disconnect after the check. i.e its possible to happen that the socket is disconnected just after it was connected "after the IsConnected check evaluated and return true", So you should wrap all communication in try/catch or try/catch/finally block expecting the socket to be disconnected at any time.