A very simple TCP server/client in C# occasionally drops packets. What can I do to prevent this? - c#

I've got an intra-PC communication server / client set up to send and receive data from one program to another - in this case, a custom server that is listening to text commands and Unity3D.
For the most part, it works, however every once in awhile, it will drop packets, and Unity will not get them without multiple attempts. The packets seem to be sent but lost, as I do see the "Sent message" console log. The following is the code for the server and client:
SERVER:
class TCPGameServer
{
public event EventHandler Error;
public Action<Data> ADelegate;
TcpListener TCPListener;
TcpClient TCPClient;
Client ActiveClient;
NetworkStream networkStream;
StreamWriter returnWriter;
StreamReader streamReader;
Timer SystemTimer = new Timer();
Timer PingTimer = new Timer();
int Port = 8637;
public TCPGameServer()
{
TCPListener = new TcpListener(IPAddress.Loopback, Port);
SystemTimer.Elapsed += StreamTimer_Tick;
SystemTimer.AutoReset = true;
SystemTimer.Interval = 2000;
PingTimer.Elapsed += PingTimer_Tick;
PingTimer.AutoReset = true;
PingTimer.Interval = 30000;
}
public void OpenListener()
{
TCPListener.Start();
TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);
Console.WriteLine("Network Open.");
}
public void GameLogout()
{
SystemTimer.AutoReset = false;
SystemTimer.Stop();
PingTimer.AutoReset = false;
PingTimer.Stop();
ActiveClient = null;
returnWriter.Dispose();
streamReader.Dispose();
Console.WriteLine("The client has logged out successfully.");
}
private void AcceptTCPCallBack(IAsyncResult asyncResult)
{
TCPClient = null;
ActiveClient = null;
returnWriter = null;
streamReader = null;
try
{
TCPClient = TCPListener.EndAcceptTcpClient(asyncResult);
TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);
ActiveClient = new Client(TCPClient);
networkStream = ActiveClient.NetworkStream;
returnWriter = new StreamWriter(TCPClient.GetStream());
streamReader = new StreamReader(TCPClient.GetStream());
Console.WriteLine("Client Connected Successfully.");
Data Packet = new Data();
Packet.cmdCommand = Command.Login;
Packet.strName = "Server";
Packet.strMessage = "LOGGEDIN";
SendMessage(Packet);
SystemTimer.AutoReset = true;
SystemTimer.Enabled = true;
SystemTimer.Start();
Ping();
PingTimer.AutoReset = true;
PingTimer.Enabled = true;
PingTimer.Start();
} catch (Exception ex)
{
OnError(TCPListener, ex);
return;
}
}
private void StreamTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
{
CheckStream();
}
private void PingTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
{
Ping();
}
private void Ping()
{
if (TCPClient.Connected)
{
Data Packet = new Data();
Packet.cmdCommand = Command.Ping;
Packet.strName = "Server";
Packet.strMessage = "PING";
SendMessage(Packet);
}
}
public void CheckStream()
{
try
{
if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
{
string PacketString = streamReader.ReadLine();
Data packet = JsonConvert.DeserializeObject<Data>(PacketString);
switch (packet.cmdCommand)
{
case Command.Logout:
GameLogout();
break;
case Command.Message:
if (ADelegate != null)
{
ADelegate(packet);
}
break;
case Command.Ping:
Console.WriteLine("PONG!");
break;
}
}
} catch (IOException e)
{
Console.WriteLine(e.Message);
} catch (NullReferenceException e)
{
Console.WriteLine(e.Message);
}
}
public void SendMessage(Data packet)
{
if (ActiveClient != null)
{
string packetMessage = JsonConvert.SerializeObject(packet);
returnWriter.WriteLine(packetMessage);
returnWriter.Flush();
}
}
public void OnError(object sender, Exception ex)
{
EventHandler handler = Error;
if (handler != null)
{
ErrorEventArgs e = new ErrorEventArgs(ex);
handler(sender, e);
}
}
public void RegisterActionDelegate(Action<Data> RegisterDelegate)
{
ADelegate += RegisterDelegate;
}
public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
{
ADelegate -= UnregisterDelegate;
}
}
CLIENT:
public class TCPNetworkClient
{
public Action<Data> PacketDelegate;
public TcpClient TCPClient;
int Port = 8637;
StreamReader streamReader;
StreamWriter streamWriter;
Timer StreamTimer = new Timer();
public bool LoggedIn = false;
public void Start()
{
if (LoggedIn == false)
{
TCPClient = null;
StreamTimer.AutoReset = true;
StreamTimer.Interval = 2000;
StreamTimer.Elapsed += StreamTimer_Tick;
try
{
TCPClient = new TcpClient("127.0.0.1", Port);
streamReader = new StreamReader(TCPClient.GetStream());
streamWriter = new StreamWriter(TCPClient.GetStream());
StreamTimer.Enabled = true;
StreamTimer.Start();
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
}
private void StreamTimer_Tick(System.Object source, System.Timers.ElapsedEventArgs e)
{
if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
{
string PacketString = streamReader.ReadLine();
Data packet = JsonConvert.DeserializeObject<Data>(PacketString);
PacketDelegate(packet);
}
}
public void Logout()
{
Data Packet = new Data();
Packet.cmdCommand = Command.Logout;
Packet.strMessage = "LOGOUT";
Packet.strName = "Game";
SendMessage(Packet);
if (streamReader != null && streamWriter != null)
{
streamReader.Dispose();
streamWriter.Dispose();
TCPClient.Close();
TCPClient = null;
streamReader = null;
streamWriter = null;
}
StreamTimer.Stop();
}
public void SendMessage(Data packet)
{
string packetMessage = JsonConvert.SerializeObject(packet);
try
{
streamWriter.WriteLine(packetMessage);
streamWriter.Flush();
} catch (Exception e)
{
}
}
public void RegisterActionDelegate(Action<Data> RegisterDelegate)
{
PacketDelegate += RegisterDelegate;
}
public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
{
PacketDelegate -= UnregisterDelegate;
}
}
I'm not really sure what's going on, or if there are any more additional checks that I need to add into the system. Note: It's TCP so that "when" this fully works, I can drop the client into other programs that I might write that may not fully rely or use Unity.

new TcpClient("127.0.0.1", Port) is not appropriate for the client. Just use TcpClient(). There is no need to specify IP and port, both of which will end up being wrong.
TCPClient.Available is almost always a bug. You seem to assume that TCP is packet based. You can't test whether a full message is incoming or not. TCP only offers a boundaryless stream of bytes. Therefore, this Available check does not tell you if a whole line is available. Also, there could be multiple lines. The correct way to read is to have a reading loop always running and simply reading lines without checking. Any line that arrives will be processed that way. No need for timers etc.
The server has the same problems.
Issue (2) might have caused the appearance of lost packets somehow. You need to fix this in any case.

Related

TcpListener does not receive data

So my homework is to write a POP3 messaging software using tcp packets and im not allowed to use external libraries. I start the tcp connection when i press the Connect button and i also have a Disconnect button which stops it. I tested my server program with PuTTY and it works fine after the first connection but when i press Disconnect and Connect again it doesnt print the received data to the monitor. This problem is bothering me for ages please help. Here is my code:
Edit:
The problem was that i dont quite understand how threads work and i didnt create a new thread for each connection so i instantiated a new thread every time i created a new listener.
namespace POP3Server
{
public partial class MainWindow : Window
{
private TcpListener server;
private Int32 port;
private IPAddress ipAddress;
private TcpClient client;
private Logger log;
private Thread tcpAcceptThread;
private bool serverStarted;
public MainWindow()
{
InitializeComponent();
log = new Logger(txtConsole);
serverStarted = false;
tcpAcceptThread = new Thread(GetData);
}
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
try
{
port = Convert.ToInt32(txtPort.Text);
ipAddress = IPAddress.Parse(txtIP.Text);
server = new TcpListener(ipAddress, port);
server.Start();
if (tcpAcceptThread.ThreadState != ThreadState.Unstarted)
tcpAcceptThread.Start();
serverStarted = true;
log.WriteLine("Server started!");
btnConnect.IsEnabled = false;
btnDisconnect.IsEnabled = true;
}
catch (Exception ex)
{
log.WriteLine(ex.ToString());
}
}
private void btnDisconnect_Click(object sender, RoutedEventArgs e)
{
try
{
if (client != null)
client.Close();
server.Stop();
log.WriteLine("Server stopped!");
serverStarted = false;
btnConnect.IsEnabled = true;
btnDisconnect.IsEnabled = false;
}
catch (Exception ex)
{
log.WriteLine(ex.ToString());
}
}
private void GetData()
{
try
{
while (serverStarted)
{
client = server.AcceptTcpClient();
this.Dispatcher.Invoke(() => log.WriteLine("Connected!"));
Byte[] bytes = new Byte[256];
String data = null;
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
this.Dispatcher.Invoke(() => log.WriteLine("Received: " + data));
}
}
}
catch (Exception ex)
{
this.Dispatcher.Invoke(() => log.WriteLine(ex.ToString()));
}
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
log.Clear();
}
}
}
Could you change following code
private bool serverStarted;
to:
private object _someLockObject = new object();
private bool _serverStarted = false;
private bool serverStarted {
get {
lock (_someLockObject)
{
return _serverStarted;
}
}
set {
lock (_someLockObject)
{
_serverStarted = value;
}
}
};
My first hunch is boolean property is not updated.

Issue with local connection between 2 programs over TCP

So I have a really really weird issue. This is going to take a tiny bit of a wall to explain fully..
I'm running some game servers for a Unity game called SCP: Secret Laboratory, and have a custom-made C# Discord Bot that helps my staff moderate both in-game and on Discord, by controlling bans, logging player warnings, etc, etc.
I'm trying to make the two platforms communicate with each other over a TCP port over the local machine address (127.0.0.1).
I've setup the listener on the bot, to whom the individual servers (there are 7) will connect to when they are ready.
I've also set it up so that if the connection is broken from either side, the servers will close the TcpClient, start a new one, and attempt to connect, while the listener just resumes listening on that port for a connection again, after closing the client on it's side.
I've tested it several times by closing the client, the socket, or just rebooting the program on either end, and they seem to without fail reconnect flawlessly.
Here's my issue..
While the game servers remain empty, everything is fine. After players start to connect, anywhere from 5mins to an hour will go by, then suddenly, seemingly at random, the server no longer 'hears' the bot when it talks across the connection, however, the bot itself does not hit an error when trying to send data, the server just never receives any. What's stranger, is the server will continue to send it's own data over the connection, and the bot does receive that data.
To attempt to 'reset' the connection with a new client when this happens, I make the servers send an initial heartbeat to the bot when they first connect. This tells the bot that the connection is established on the server's end, and begins a looping thread that will send an AYT message to the server every 10s, to which the server must reply.
If the bot sends 3 AYT messages without getting a response back, it will close the TcpClient, and start listening for a new one.
At this time, the server detects the connection was closed, disposes of it's client, instantiates a new one, and tries to connect successfully. The bot then receives it's initial heartbeat, and starts the AYT timer for that client again, but the server continues to not receive them. Or any data whatsoever sent from the bot, even though the bot still receives data from the server during this time.
The only solution to fix the problem at that point, is to fully restart the game server, after which it will connect to the bot and work perfectly fine, until it.. just doesn't anymore.
For reference, pastebins of the code used are below.
The bot "listener" side
public class ProcessSTT
{
private static ConcurrentDictionary<int, TcpClient> bag = new ConcurrentDictionary<int, TcpClient>();
private static ConcurrentDictionary<int, int> heartbeats = new ConcurrentDictionary<int, int>();
public static void SendData(string data, int port, ulong channel = 0)
{
try
{
BinaryFormatter formatter = new BinaryFormatter();
SerializedData.SerializedData serializedData =
new SerializedData.SerializedData { Data = data, Port = port, Channel = channel };
//Console.WriteLine($"Sending {serializedData.Data}");
if (!bag.ContainsKey(port))
{
Console.WriteLine($"STT: Bag does not contain {port}");
return;
}
if (bag[port] != null && bag[port].Connected)
formatter.Serialize(bag[port].GetStream(), serializedData);
else
{
Console.WriteLine($"Error - Bag {port} is null or not connected.");
if (bag.TryRemove(port, out TcpClient client))
client.Dispose();
}
}
catch (IOException s)
{
Console.WriteLine($"STT: Socket exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Key == port)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static List<TcpListener> listener = new List<TcpListener>();
public static void Init()
{
for (int i = 1; i < 8; i++)
{
TcpListener list = new TcpListener(IPAddress.Loopback, 11900 + i);
Console.WriteLine($"STT: Listener started for port {11900 + i}");
listener.Add(list);
list.Start();
ThreadPool.QueueUserWorkItem(ListenForConn, list);
}
}
public static async Task Heartbeat(int port)
{
await Task.Delay(10000);
for (;;)
{
Console.WriteLine("STT: Starting Heartbeat");
if (heartbeats[port] > 3)
{
Console.WriteLine($"STT: Removing {port} due to heartbeat timeout.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
heartbeats.TryRemove(port, out int _);
return;
}
heartbeats[port]++;
Console.WriteLine($"STT: Sending heartbeat to: {port}");
if (!bag[port].Connected)
{
Console.WriteLine($"STT: {port} is null, removing.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
return;
}
SendData("ping", port, 653737934150959115);
await Task.Delay(10000);
}
}
public static void ListenForConn(object token)
{
Console.WriteLine("STT: Listener started.");
TcpListener listen = token as TcpListener;
for (;;)
{
try
{
TcpClient thing = listen.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(ListenOn, thing);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public static async Task ReceiveData(SerializedData.SerializedData data, TcpClient client)
{
try
{
if (data == null)
{
Console.WriteLine("STT: Received data null");
return;
}
if (data.Data == "ping")
{
if (!bag.ContainsKey(data.Port))
{
Console.WriteLine($"STT: Adding {data.Port}");
bag.TryAdd(data.Port, client);
}
if (!bag[data.Port].Connected || bag[data.Port] == null)
{
Console.WriteLine($"STT: Bag {data.Port} not connected or null, removing.");
if (bag.TryRemove(data.Port, out TcpClient cli))
{
cli?.Close();
}
}
Console.WriteLine($"STT: Received heartbeat for: {data.Port}");
if (!heartbeats.ContainsKey(data.Port))
{
Heartbeat(data.Port);
heartbeats.TryAdd(data.Port, 0);
}
else
heartbeats[data.Port]--;
return;
}
Console.WriteLine(data.Data);
data.Data = data.Data.Substring(data.Data.IndexOf('#') + 1);
//Console.WriteLine("Getting guild.");
SocketGuild guild = Bot.Discord.GetGuild(478381106798788639);
//Console.WriteLine("Getting channel");
SocketTextChannel chan = guild.GetTextChannel(data.Channel);
//Console.WriteLine("Sending message.");
await chan.SendMessageAsync($"Server {data.Port -= 7770}: {data.Data}");
if (data.Port == 7771)
{
DiscordWebhookClient webhook = new DiscordWebhookClient(
"");
await webhook.SendMessageAsync($"{data.Data}");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public static void ListenOn(object token)
{
TcpClient client = token as TcpClient;
try
{
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData serializedData;
if (!client.Connected)
{
Console.WriteLine($"Client not connected..");
client.Close();
continue;
}
serializedData = formatter.Deserialize(client.GetStream()) as SerializedData.SerializedData;
new Thread(() => ReceiveData(serializedData, client)).Start()
}
}
catch (SerializationException s)
{
Console.WriteLine($"STT: Serialization exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Value == client)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
The server "speaker" side
public class ProcessSTT
{
private static TcpClient tcpClient;
public static readonly ConcurrentQueue<SerializedData.SerializedData> dataQueue = new ConcurrentQueue<SerializedData.SerializedData>();
private static Thread _init;
private static bool _locked;
public static void Init()
{
if (_locked)
return;
_locked = true;
Thread.Sleep(1000);
try
{
Plugin.Log($"STT: Starting INIT.");
tcpClient?.Close();
tcpClient = new TcpClient();
while (!tcpClient.Connected)
{
Plugin.Log($"STT: While loop start");
Thread.Sleep(2000);
try
{
tcpClient.Connect("127.0.0.1", ServerConsole.Port + 4130);
}
catch (SocketException)
{
tcpClient.Client.Disconnect(false);
}
catch (Exception e)
{
Plugin.Log($"STT: {e}");
}
}
Thread thread = new Thread(ReceiveData);
thread.Start();
SendData("ping", 0);
_locked = false;
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void SendData(string data, ulong channel)
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
SerializedData.SerializedData serializedData = new SerializedData.SerializedData
{
Data = data, Port = ServerConsole.Port, Channel = channel
};
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(tcpClient.GetStream(), serializedData);
ServerConsole.AddLog($"Sent {data}");
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void ReceiveData()
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData deserialize =
formatter.Deserialize(tcpClient.GetStream()) as SerializedData.SerializedData;
if (deserialize == null)
continue;
dataQueue.Enqueue(deserialize);
}
}
catch (SerializationException s)
{
_init = new Thread(Init);
_init.Start();
}
catch (IOException e)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public class HandleQueue
{
public static ulong channelid;
public static void HandleQueuedItems()
{
while (ProcessSTT.dataQueue.TryDequeue(out SerializedData.SerializedData result))
{
string command = result.Data;
Plugin.Log($"STT: Received {result.Data} for {result.Port}");
if (result.Port != ServerConsole.Port)
return;
if (result.Data == "ping")
{
Plugin.Log("STT: BLART Heartbeat received.");
ProcessSTT.SendData("ping", 0);
return;
}
channelid = result.Channel;
try
{
GameCore.Console.singleton.TypeCommand($"/{command}", new BlartSender());
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public static IEnumerator<float> Handle()
{
for (;;)
{
HandleQueuedItems();
yield return Timing.WaitForSeconds(1f);
}
}
}
public class BlartSender : CommandSender
{
public override void RaReply(string text, bool success, bool logToConsole, string overrideDisplay)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override void Print(string text)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override string SenderId => "BLART";
public override string Nickname => "BLART";
public override ulong Permissions => ServerStatic.GetPermissionsHandler().FullPerm;
public override byte KickPower => Byte.MaxValue;
public override bool FullPermissions => true;
}`

"A socket operation failed because the destination host was down" when connecting pc to mobie device

My project is to connecting pc with mobile device using 32feet.net library.
Until Now, my solution is doing steps like:
Scanning for devices in area.
pairing with selected device .
request pairing with mobile device and when it's okay this error appear :
"A socket operation failed because the destination host was down" and I don't know why and how to overcome this problem. Any help with this problem will be appreciated.
And my solution is:
namespace Bluetooth
{
public partial class Form1 : Form
{
List<string> items;
public Form1()
{
items = new List<string>();
InitializeComponent();
}
private void Connect_Click(object sender, EventArgs e)
{
if(ServerStarted )
{
updateUI("Server Already Started");
return;
}
if(rbClient.Checked)
{
//ConnectAsClient();
StartScan();
}
else
{
ConnectAsServer();
}
}
private void StartScan()
{
listBox.DataSource = null;
listBox.Items.Clear();
items.Clear();
Thread bluethoothscanthread = new Thread(new ThreadStart(scan));
bluethoothscanthread.Start();
}
BluetoothDeviceInfo[] devices;
private void scan()
{
updateUI("Starting Scan ...");
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
updateUI("Scan Completed......");
updateUI(devices.Length.ToString() + "devices discovered");
foreach(BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
}
UpdateDeviceList();
}
private void ConnectAsServer()
{
Thread BluethoothServerThread = new Thread(new ThreadStart(ServerConnectThread));
BluethoothServerThread.Start();
}
private void ConnectAsClient()
{
throw new NotImplementedException();
}
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
bool ServerStarted = false;
public void ServerConnectThread()
{
ServerStarted = true;
updateUI("Server Started , Waiting For Clients ");
BluetoothListener bluelistner = new BluetoothListener(mUUID);
bluelistner.Start();
BluetoothClient conn = bluelistner.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mstream = conn.GetStream();
while(true)
{
// handle server connection
try
{
byte[] Received = new byte[1024];
mstream.Read(Received, 0, Received.Length);
updateUI("Received : " + Encoding.ASCII.GetString(Received));
byte[] Sent = Encoding.ASCII.GetBytes("Hello World ");
mstream.Write(Sent, 0, Sent.Length);
}
catch(IOException exception)
{
updateUI("Client has disconnected !!! ");
}
}
}
private void updateUI(string message)
{
Func<int> del = delegate ()
{
tbOutput.AppendText(message + System.Environment.NewLine);
return 0 ;
};
Invoke(del);
}
private void UpdateDeviceList()
{
Func<int> del = delegate ()
{
listBox.DataSource = items;
return 0;
};
Invoke(del);
}
BluetoothDeviceInfo deviceinfo;
private void listBox_DoubleClick(object sender, EventArgs e)
{
deviceinfo = devices.ElementAt(listBox.SelectedIndex);
updateUI(deviceinfo.DeviceName + "Was Selected , attempting connect");
if( pairDevice())
{
updateUI("Device Paired.....");
updateUI("Starting Connect Thread");
Thread bluethoothClientThread = new Thread(new ThreadStart(ClientconnectThread));
bluethoothClientThread.Start();
}
else
{
updateUI("Pair Failed");
}
}
private void ClientconnectThread()
{
BluetoothClient client = new BluetoothClient();
updateUI("Attempting Connect");
client.BeginConnect(deviceinfo.DeviceAddress, mUUID, this.BluetoothClientConnectCallback, client);
}
void BluetoothClientConnectCallback(IAsyncResult result)
{
BluetoothClient client = (BluetoothClient)result.AsyncState;
BluetoothEndPoint bep = new BluetoothEndPoint(deviceinfo.DeviceAddress, BluetoothService.AvctpProtocol);
BluetoothClient cli = new BluetoothClient();
client.Connect(bep); // the exception throw in this line //
Stream stream = cli.GetStream();
stream.ReadTimeout = 10000;
//Stream peerStream = cli.GetStream();
// client.Connect(BluetoothAddress.Parse(items(0)), mUUID);
//client.EndConnect(result);
//while(true)
//{
// while (!ready) ;
// stream.Write(message, 0, message.Length);
//}
//streaming(result);
if(result.IsCompleted)
{
// client.EndConnect(result);
// Stream stream = client.GetStream();
// stream.ReadTimeout = 1000;
updateUI("Ya rabbbbb");
//while (!ready) ;
//stream.Write(message, 0, message.Length);
}
}
void streaming (IAsyncResult result)
{
BluetoothListener listn = new BluetoothListener(mUUID);
listn.Start();
BluetoothClient conn = listn.AcceptBluetoothClient();
Stream peerstream = conn.GetStream();
bool rightconn = peerstream.CanRead;
if(rightconn)
{
updateUI("Now you can streaming");
}
else
{
updateUI("Not Yet");
}
}
string mypin = "1234";
private bool pairDevice()
{
if(!deviceinfo.Authenticated)
{
if (!BluetoothSecurity.PairRequest(deviceinfo.DeviceAddress, mypin))
{
return false;
}
}
return true;
}
bool ready = false;
byte[] message;
private void tbText_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == 13)
{
message = Encoding.ASCII.GetBytes(tbText.Text);
ready = true;
tbText.Clear();
}
}
}
} // End of namespace
I think I followed same example with you and of course I encountered same problem.
Rest of my codes are same with yours until "private void ClientconnectThread()" function and here is mine;
private void ClientConnectThread()
{
BluetoothClient client = new BluetoothClient();
updateUI("attempting connect");
//client.BeginConnect(deviceInfo.DeviceAddress, mUUID, this.BluetoothClientConnectCallback, client);
BluetoothAddress addressSeleccionado = deviceInfo.DeviceAddress;
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
BluetoothEndPoint ep = new BluetoothEndPoint(addressSeleccionado, mUUID);
client.Connect(ep);
updateUI("connected");
Stream stream = client.GetStream();
while (true)
{
byte[] received = new byte[1024];
stream.Read(received, 0, received.Length);
updateUI("received: ");
for (int i = 0; i < 15; i++)
{
updateUI(received[i].ToString());
}
if (ready)
{
ready = false;
stream.Write(message, 0, message.Length);
}
}
}
With this code I can connect the remote device and implement basic send/receive data communication. In order to my search over internet, "if this code does not fit for you", you might think that 32feet is not compatible with your bluetooth driver. (by the way my PC's OS is 64bit Win7 and I use the internal BT device of my PC )
Best regards.

Why does my streamreader and writer suddenly stop working?

Ok, so I'm attempting to create a simple Chat application over TCP/IP for a group of friends of mine who play DnD online. Eventually I want to add more features, but for now I just want the chat to work!!
Here is the code I have for the Main Server
class MainServer
{
IPAddress m_address = IPAddress.Parse("127.0.0.1");
Int32 m_port = 5550;
public static Hashtable userNicknames = new Hashtable(50);
public static Hashtable connectionToNick = new Hashtable(50);
public MainServer()
{
TcpListener listener = new TcpListener(m_address, m_port);
Thread listenThread = new Thread(new ParameterizedThreadStart(StartListening));
listenThread.Start(listener);
Console.WriteLine("Listening for incoming connection requests...");
}
private void StartListening(Object listener)
{
TcpListener server = (TcpListener)listener;
ClientCommCenter commC;
server.Start();
while (true)
{
if (server.Pending())
{
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Client has connected...");
commC = new ClientCommCenter(client);
}
}
}
public static void SendSystemMessage(string msg)
{
StreamWriter writer;
TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count];
MainServer.userNicknames.Values.CopyTo(connectedClients, 0);
for (int ii = 0; ii < connectedClients.Length; ii++)
{
try
{
if (msg.Trim().Equals(String.Empty))
continue;
writer = new StreamWriter(connectedClients[ii].GetStream());
writer.WriteLine("Message from server: " + msg);
writer.Flush();
writer = null;
}
catch (Exception e)
{
MainServer.userNicknames.Remove(MainServer.connectionToNick[connectedClients[ii]]);
MainServer.connectionToNick.Remove(connectedClients[ii]);
}
}
}
public static void SendMessageToAll(string nickname, string msg)
{
StreamWriter writer;
TcpClient[] connectedClients = new TcpClient[MainServer.userNicknames.Count];
MainServer.userNicknames.Values.CopyTo(connectedClients, 0);
for (int ii = 0; ii < connectedClients.Length; ii++)
{
try
{
if (msg.Trim().Equals(String.Empty))
continue;
writer = new StreamWriter(connectedClients[ii].GetStream());
writer.WriteLine(nickname + ": " + msg);
writer.Flush();
writer = null;
}
catch (Exception e)
{
String user = (string)MainServer.connectionToNick[connectedClients[ii]];
SendSystemMessage("ATTENTION: " + user + " has disconnected from chat");
MainServer.userNicknames.Remove(user);
MainServer.connectionToNick.Remove(connectedClients[ii]);
}
}
}
}
Here is the main communication class, used separately by each client
class ClientCommCenter
{
TcpClient m_client;
StreamReader m_reader;
StreamWriter m_writer;
String m_nickname;
public ClientCommCenter(TcpClient client)
{
m_client = client;
Thread chatThread = new Thread(new ThreadStart(StartChat));
chatThread.Start();
}
private String GetNick()
{
m_writer.WriteLine("Enter a nickname to begin.");
m_writer.Flush();
return m_reader.ReadLine();
}
private void StartChat()
{
m_reader = new StreamReader(m_client.GetStream());
m_writer = new StreamWriter(m_client.GetStream());
m_writer.WriteLine("Connected to DnD Chat!!");
m_nickname = GetNick();
while (MainServer.userNicknames.Contains(m_nickname))
{
m_writer.WriteLine("ERROR!!! Username already in use");
m_nickname = GetNick();
}
MainServer.userNicknames.Add(m_nickname, m_client);
MainServer.connectionToNick.Add(m_client, m_nickname);
MainServer.SendSystemMessage("****** " + m_nickname + " ****** has joined the chat!");
m_writer.WriteLine("Now connected....");
m_writer.Flush();
Thread startChatting = new Thread(new ThreadStart(runChat));
startChatting.Start();
}
private void runChat()
{
try
{
String clientMessage = String.Empty;
while(true){
clientMessage = m_reader.ReadLine();
MainServer.SendMessageToAll(m_nickname, clientMessage);
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
}
And finally, here is the code for the Client class:
public partial class MainForm : Form
{
[DllImport("kernel32.dll")]
private static extern void ExitProcess(int a);
TcpClient client;
StreamReader m_reader;
StreamWriter m_writer;
public MainForm()
{
InitializeComponent();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = false;
Application.Exit();
if (m_reader != null)
{
m_reader.Dispose();
}
ExitProcess(0);
}
private void MainForm_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
SendChat();
}
}
private void SendChat()
{
TextBox txtChat = (TextBox)chatEntry;
if (chatEntry.Lines.Length >= 1)
{
m_writer.WriteLine(txtChat.Text);
m_writer.Flush();
chatEntry.Text = String.Empty;
chatEntry.Lines = null;
}
}
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
while (true)
{
Application.DoEvents();
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker( delegate{
RunChat();
}));
}
if (reader.Peek() > 0)
{
chatDisplay.AppendText(reader.ReadLine() + "\r\n");
chatDisplay.SelectionStart = chatDisplay.Text.Length;
}
}
}
private void toolstripConnectButton_Click(object sender, EventArgs e)
{
client = new TcpClient("127.0.0.1", 5550);
m_writer = new StreamWriter(client.GetStream());
m_reader = new StreamReader(client.GetStream());
Thread chatThread = new Thread(new ThreadStart(RunChat));
chatThread.Start();
while (true)
{
Application.DoEvents();
}
}
private void sendButton_Click(object sender, EventArgs e)
{
SendChat();
}
}
The problem that I am having with the above code is this: I can connect to the running server perfectly fine, and I am correctly prompted by the server that I have connected, and it then prompts me for a nickname.
I type the nickname into the text box and press send. After this occurs however, I stop receiving messages from the server all together. Literally nothing. I can even spam the connect button and it constantly shows up with the same two messages:
"Connected"
"Enter a nickname"
I have been trying to figure this out for close to 5 hours now, and I simply have no idea what is going on. I have a feeling it is something incredibly simple, as the solution is ALWAYS simple.
So, generous people of SO, can you figure out my problem? Why does my streamreader and streamwriter suddenly stop working?!?!?!
Two things:
First, skip the if (reader.Peek() > 0). Just call reader.ReadLine(); this will block until you have a line available. I am not sure why, but even after sending the message, Peek is returning -1, but ReadLine returns a line at that point, fixing the problem. Anyway, spinning around on Application.DoEvents() is not helping matters.
(Similarly, you can skip if (server.Pending())).
Second, your use of Invoke is faulty; you should not be "Invoking" RunChat() because that is the method that repeatedly polls the stream for new data. This means you will run the entire method on the UI thread, which is precisely what you want to avoid. The UI is busy pumping the Windows message queue. You should "Invoke" only the code that modifies the control's properties.
(I suspect that is why you found it necessary to use Application.DoEvents() anyway. You shouldn't need it if you are handling your threading correctly.)
(Also, the first thing you should do is to check InvokeRequired. As your method is now, you're creating a StreamReader that you can never use. There are other places where you do that, but that's off topic.)
Here are two suggestions:
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
Delegate invoker = new Action<string>(AppendChatText);
while (true)
Invoke(invoker, reader.ReadLine());
}
or, to use the more classic "invoke" pattern:
private void RunChat()
{
StreamReader reader = new StreamReader(client.GetStream());
while (true)
AppendChatText(reader.ReadLine());
}
private void AppendChatText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((Action<string>)AppendChatText, text);
return;
}
chatDisplay.AppendText(text + "\r\n");
chatDisplay.SelectionStart = chatDisplay.Text.Length;
}
The first has the advantage of creating only one Delegate object; the second creates a new one each time.
Finally, this is a very C# 1.2 approach to the problem. A more up-to-date approach would use async/await to avoid creating all those threads (not to mention System.Collections.Generic.Dictionary<,> instead of HashTable).

Async chat server buffer issue

can someone please help me out with this... I've been struggling all day.
So I'm trying to learn Async sockets which is something that's been giving me trouble.
The issue is basically the way I'm updating the ListBox with people who have joined the chat room's names:
Basically what I'm doing is having each client send "!!addlist [nickname]" when they join the server.
It's not ideal as it doesn't check for duplicates etc. but now I just want to know why it won't work.
Whenever somebody adds a name they haven't seen before, they will also send "!!addlist [nick]"
In this way, every time someone joins, the lists should be updated for everyone.
The issue seems to be that all the clients start communicating at the same time and it interferes with the buffer.
I've tried using a separate buffer for every client so that's not the issue.
I've tried using lock() but that doesn't seem to be working either.
Essentially what happens is the buffers seem to truncate; where there is data from two different people in the same buffer.
Please just tell me what I'm doing wrong with the buffers or on the client side:
Note that the async socket is using Send instead of BeginSend.
I've tried both methods and they run into the same issue... so it's probably client side?
public partial class Login : Form
{
private ChatWindow cw;
private Socket serverSocket;
private List<Socket> socketList;
private byte[] buffer;
private bool isHost;
private bool isClosing;
public void startListening()
{
try
{
this.isHost = true; //We're hosting this server
cw.callingForm = this; //Give ChatForm the login form (this) [that acts as the server]
cw.Show(); //Show ChatForm
cw.isHost = true; //Tell ChatForm it is the host (for display purposes)
this.Hide(); //And hide the login form
serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text))); //Bind to our local address
serverSocket.Listen(1); //And start listening
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //When someone connects, begin the async callback
cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text); //And have ChatForm connect to the server
}
catch (Exception) { /*MessageBox.Show("Error:\n\n" + e.ToString());*/ } //Let us know if we ran into any errors
}
public void AcceptCallback(IAsyncResult AR)
{
try
{
Socket s = serverSocket.EndAccept(AR); //When someone connects, accept the new socket
socketList.Add(s); //Add it to our list of clients
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s); //Begin the async receive method using our buffer
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //And start accepting new connections
}
catch (Exception) {}
}
public void ReceiveCallback(IAsyncResult AR) //When a message from a client is received
{
try
{
if (isClosing)
return;
Socket s = (Socket)AR.AsyncState; //Get the socket from our IAsyncResult
int received = s.EndReceive(AR); //Read the number of bytes received (*need to add locking code here*)
byte[] dbuf = new byte[received]; //Create a temporary buffer to store just what was received so we don't have extra data
Array.Copy(buffer, dbuf, received); //Copy the received data from our buffer to our temporary buffer
foreach (Socket client in socketList) //For each client that is connected
{
try
{
if (client != (Socket)AR.AsyncState) //If this isn't the same client that just sent a message (*client handles displaying these*)
client.Send(dbuf); //Send the message to the client
}
catch (Exception) { }
} //Start receiving new data again
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
public void SendCallback(IAsyncResult AR)
{
try
{
Socket s = (Socket)AR.AsyncState;
s.EndSend(AR);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
Here is the client side:
public void getData()
{
try
{
byte[] buf = new byte[1024];
string message = "";
while(isConnected)
{
Array.Clear(buf, 0, buf.Length);
message = "";
clientSocket.Receive(buf, buf.Length, SocketFlags.None);
message = Encoding.ASCII.GetString(buf);
if (message.StartsWith("!!addlist"))
{
message = message.Replace("!!addlist", "");
string userNick = message.Trim();
if (!namesBox.Items.Contains(userNick))
{
addNick(userNick.Trim());
}
continue;
}
else if (message.StartsWith("!!removelist"))
{
message = message.Replace("!!removelist", "");
string userNick = message.Trim();
removeNick(userNick);
output("Someone left the room: " + userNick);
continue;
}
else if (!namesBox.Items.Contains(message.Substring(0, message.IndexOf(":"))))
{
addNick(message.Substring(0, message.IndexOf(":")).Trim()); //So they at least get added when they send a message
}
output(message);
}
}
catch (Exception)
{
output("\n\nConnection to the server lost.");
isConnected = false;
}
}
Here is my addNick function that seems to fix some things?
public void addNick(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
output("Someone new joined the room: " + n);
sendRaw("!!addlist " + nickName);
}
}
I think the issue is that some of the packets are being skipped?
Maybe there's too much code in the client after Receive before it gets called again?
Should I create a separate thread for each message so that receive runs constantly? (Dumb)
Should I have my client use Async receives and sends as well?
I have a feeling that is the answer ^
With all of the checks I do, I managed to clean up the duplicate name issue... but i regularly receive messages with spaces and partial messages from other clients it seems.
Okay so, after messing with this for a long time, I have it relatively stable.
For starters, I added the following state object:
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public bool newConnection = true;
}
This makes it easy to keep track of each connection and gives each connection its own buffer.
The second thing I did was look for a new line in each message.
I wasn't looking for this in the original code and I believe this was the root of most issues.
I also gave the responsibility of dealing with username management to the server; something that I should have done from the start obviously.
Here is the current server code:
This code is in no way perfect and I'm continuously finding new errors the more I try to break it. I'm going to keep messing with it for awhile but at the moment, it seems to work decently.
public partial class Login : Form
{
private ChatWindow cw;
private Socket serverSocket;
private List<Socket> socketList;
private byte[] buffer;
private bool isHost;
private bool isClosing;
private ListBox usernames;
public Login()
{
InitializeComponent();
}
private void Login_Load(object sender, EventArgs e)
{
ipLabel.Text = getLocalIP();
cw = new ChatWindow();
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketList = new List<Socket>();
buffer = new byte[1024];
isClosing = false;
usernames = new ListBox();
}
public string getLocalIP()
{
return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
}
private void joinButton_Click(object sender, EventArgs e)
{
try
{
int tryPort = 0;
this.isHost = false;
cw.callingForm = this;
if (ipBox.Text == "" || portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort))
{
MessageBox.Show("You must enter an IP Address, Port, and Nickname to connect to a server.", "Missing Info");
return;
}
this.Hide();
cw.Show();
cw.connectTo(ipBox.Text, int.Parse(portBox.Text), nicknameBox.Text);
}
catch(Exception otheree) {
MessageBox.Show("Error:\n\n" + otheree.ToString(),"Error connecting...");
cw.Hide();
this.Show();
}
}
private void hostButton_Click(object sender, EventArgs e)
{
int tryPort = 0;
if (portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort)) {
MessageBox.Show("You must enter a Port and Nickname to host a server.", "Missing Info");
return;
}
startListening();
}
public void startListening()
{
try
{
this.isHost = true; //We're hosting this server
cw.callingForm = this; //Give ChatForm the login form (this) [that acts as the server]
cw.Show(); //Show ChatForm
cw.isHost = true; //Tell ChatForm it is the host (for display purposes)
this.Hide(); //And hide the login form
serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text))); //Bind to our local address
serverSocket.Listen(1); //And start listening
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //When someone connects, begin the async callback
cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text); //And have ChatForm connect to the server
}
catch (Exception) {}
}
public void AcceptCallback(IAsyncResult AR)
{
try
{
StateObject state = new StateObject();
state.workSocket = serverSocket.EndAccept(AR);
socketList.Add(state.workSocket);
state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
catch (Exception) {}
}
public void ReceiveCallback(IAsyncResult AR)
{
try
{
if (isClosing)
return;
StateObject state = (StateObject)AR.AsyncState;
Socket s = state.workSocket;
String content = "";
int received = s.EndReceive(AR);
if(received > 0)
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received));
content = state.sb.ToString();
if (content.IndexOf(Environment.NewLine) > -1) //If we've received the end of the message
{
if (content.StartsWith("!!addlist") && state.newConnection)
{
state.newConnection = false;
content = content.Replace("!!addlist", "");
string userNick = content.Trim();
if (isHost && userNick.StartsWith("!"))
userNick = userNick.Replace("!", "");
userNick = userNick.Trim();
if (userNick.StartsWith("!") || userNick == string.Empty || usernames.Items.IndexOf(userNick) > -1)
{
//Invalid Username :c get dropped
s.Send(Encoding.ASCII.GetBytes("Invalid Username/In Use - Sorry :("));
s.Shutdown(SocketShutdown.Both);
s.Disconnect(false);
s.Close();
socketList.Remove(s);
return;
}
usernames.Items.Add(userNick);
foreach (string name in usernames.Items)
{
if (name.IndexOf(userNick) < 0)
{
s.Send(Encoding.ASCII.GetBytes("!!addlist " + name + "\r\n"));
Thread.Sleep(10); //such a hack... ugh it annoys me that this works
}
}
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(Encoding.ASCII.GetBytes("!!addlist " + userNick + "\r\n"));
}
catch (Exception) { }
}
}
else if (content.StartsWith("!!removelist") && !state.newConnection)
{
content = content.Replace("!!removelist", "");
string userNick = content.Trim();
usernames.Items.Remove(userNick);
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(Encoding.ASCII.GetBytes("!!removelist " + userNick + "\r\n"));
}
catch (Exception) { }
}
}
else if (state.newConnection) //if they don't give their name and try to send data, just drop.
{
s.Shutdown(SocketShutdown.Both);
s.Disconnect(false);
s.Close();
socketList.Remove(s);
return;
}
else
{
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(System.Text.Encoding.ASCII.GetBytes(content));
}
catch (Exception) { }
}
}
}
Array.Clear(state.buffer, 0, StateObject.BufferSize);
state.sb.Clear();
s.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception) {
socketList.Remove(((StateObject)AR.AsyncState).workSocket);
}
}
public void SendCallback(IAsyncResult AR)
{
try
{
StateObject state = (StateObject)AR.AsyncState;
state.workSocket.EndSend(AR);
}
catch (Exception) {}
}
private void Login_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
this.isClosing = true;
if (this.isHost)
{
foreach (Socket c in socketList)
{
if (c.Connected)
{
c.Close();
}
}
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Close();
serverSocket = null;
serverSocket.Dispose();
}
socketList.Clear();
}
catch (Exception) { }
finally
{
Application.Exit();
}
}
}
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public bool newConnection = true;
}
The client code (work in progress):
public partial class ChatWindow : Form
{
private Socket clientSocket;
private Thread chatThread;
private string ipAddress;
private int port;
private bool isConnected;
private string nickName;
public bool isHost;
public Login callingForm;
private static object conLock = new object();
public ChatWindow()
{
InitializeComponent();
isConnected = false;
isHost = false;
}
public string getIP() {
return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
}
public void displayError(string err)
{
output(Environment.NewLine + Environment.NewLine + err + Environment.NewLine);
}
public void op(string s)
{
try
{
lock (conLock)
{
chatBox.Text += s;
}
}
catch (Exception) { }
}
public void connectTo(string ip, int p, string n) {
try
{
this.Text = "Trying to connect to " + ip + ":" + p + "...";
this.ipAddress = ip;
this.port = p;
this.nickName = n;
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (!isHost)
{
op("Connecting to " + ipAddress + ":" + port + "...");
}
else
{
output("Listening on " + getIP() + ":" + port + "...");
}
clientSocket.Connect(ipAddress, port);
isConnected = true;
if (!isHost)
{
this.Text = "Connected to " + ipAddress + ":" + port + " - Nickname: " + nickName;
output("Connected!");
}
else
{
this.Text = "Hosting on " + getIP() + ":" + port + " - Nickname: " + nickName;
}
chatThread = new Thread(new ThreadStart(getData));
chatThread.Start();
nickName = nickName.Replace(" ", "");
nickName = nickName.Replace(":", "");
if(nickName.StartsWith("!"))
nickName = nickName.Replace("!", "");
namesBox.Items.Add(nickName);
sendRaw("!!addlist " + nickName);
}
catch (ThreadAbortException)
{
//do nothing; probably closing chat window
}
catch (Exception e)
{
if (!isConnected)
{
this.Hide();
callingForm.Show();
clearText();
MessageBox.Show("Error:\n\n" + e.ToString(), "Error connecting to remote host");
}
}
}
public void removeNick(string n)
{
if (namesBox.Items.Count <= 0)
return;
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
namesBox.Items.RemoveAt(x);
}
public void clearText()
{
try
{
lock (conLock)
{
chatBox.Text = "";
}
}
catch (Exception) { }
}
public void addNick(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
output("Someone new joined the room: " + n);
//sendRaw("!!addlist " + nickName);
}
}
public void addNickNoMessage(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
//sendRaw("!!addlist " + nickName);
}
}
public void getData()
{
try
{
byte[] buf = new byte[1024];
string message = "";
while(isConnected)
{
Array.Clear(buf, 0, buf.Length);
message = "";
int gotData = clientSocket.Receive(buf, buf.Length, SocketFlags.None);
if (gotData == 0)
throw new Exception("I swear, this was working before but isn't anymore...");
message = Encoding.ASCII.GetString(buf);
if (message.StartsWith("!!addlist"))
{
message = message.Replace("!!addlist", "");
string userNick = message.Trim();
if(!namesBox.Items.Contains(userNick))
{
addNick(userNick);
}
continue;
}
else if (message.StartsWith("!!removelist"))
{
message = message.Replace("!!removelist", "");
string userNick = message.Trim();
removeNick(userNick);
output("Someone left the room: " + userNick);
continue;
}
output(message);
}
}
catch (Exception)
{
isConnected = false;
output(Environment.NewLine + "Connection to the server lost.");
}
}
public void output(string s)
{
try
{
lock (conLock)
{
chatBox.Text += s + Environment.NewLine;
}
}
catch (Exception) { }
}
private void ChatWindow_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
if(isConnected)
sendRaw("!!removelist " + nickName);
isConnected = false;
clientSocket.Shutdown(SocketShutdown.Receive);
if (chatThread.IsAlive)
chatThread.Abort();
callingForm.Close();
}
catch (Exception) { }
}
private void sendButton_Click(object sender, EventArgs e)
{
if(isConnected)
send(sendBox.Text);
}
private void sendBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (isConnected)
{
if (sendBox.Text != "")
{
send(sendBox.Text);
sendBox.SelectAll();
e.SuppressKeyPress = true;
e.Handled = true;
}
}
}
}
private void send(string t) {
try
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(nickName + ": " + t + "\r\n");
clientSocket.Send(data);
output(nickName + ": " + t);
}
catch (Exception e)
{
displayError(e.ToString());
}
}
private void sendRaw(string t)
{
try
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(t + "\r\n");
clientSocket.Send(data);
}
catch (Exception e)
{
displayError(e.ToString());
}
}
private void chatBox_TextChanged(object sender, EventArgs e)
{
chatBox.SelectionStart = chatBox.Text.Length;
chatBox.ScrollToCaret();
}
private void sendBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
e.SuppressKeyPress = true;
}
}
To do:
Add invokes, more delegates, do some more QA and find out what breaks it.
Also, I believe there's still the possibility of packet loss due to the client addlist functions being in the read loop. I believe this is why the "crappy hack" using Thread.Sleep(10) in the server callback for name population is an issue.
I think it might be better to either pass the command off to another thread while continuing to read or have the client tell the server it's ready for another name.
Otherwise, there might be some data loss during name updates.
The other thing is that, as was said in the comments above, delegates should be used when updating the UI objects (chatbox and listbox). I wrote the code for these but ultimately removed it because there was no noticeable change and I wanted to keep it simple.
I do still use an object lock when outputting text to the chatbox, but there's no noticeable difference there.
The code should be added as not using delegates is potentially problematic, but I literally caught the chat box in an infinite loop of updates without issue.
I tried breaking it with telnet and was successful so I added a newConnection property to the StateObject to ensure that each client can only send "!!addlist" once.
There are, of course, other ways to abuse the server by creating a client that joins and leaves repeatedly, so ultimately I will probably end up passing the !!removelist handling to the server instead of leaving it up to the client.

Categories

Resources