I have been following an old tutorial for making a chat program and I have been dissecting it to fit into a new form, although I have gotten it to work as it was intended it runs into the error: "Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall."
that points to this part of the code.
while (Connected)
{
// Show the messages in the log TextBox
this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });
}
I only get the error upon closing the client, or disconnecting.
this is the majority of the client code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace Table_Top_RPG
{
public partial class Connect : Form
{
// Will hold the user name
private string UserName = "Unknown";
public static StreamWriter swSender;
private StreamReader srReceiver;
private TcpClient tcpServer;
// Needed to update the form with messages from another thread
private delegate void UpdateLogCallback(string strMessage);
// Needed to set the form to a "disconnected" state from another thread
private delegate void CloseConnectionCallback(string strReason);
private Thread thrMessaging;
private IPAddress ipAddr;
private bool Connected;
public Connect()
{
// On application exit, don't forget to disconnect first
Application.ApplicationExit += new EventHandler(OnApplicationExit);
InitializeComponent();
}
private void BtnConnect_Click(object sender, EventArgs e)
{
// If we are not currently connected but awaiting to connect
if (Connected == false)
{
InitializeConnection();
}
else // We are connected, thus disconnect
{
CloseConnection("Disconnected at user's request.");
}
}
// The event handler for application exit
public void OnApplicationExit(object sender, EventArgs e)
{
if (Connected == true)
{
// Closes the connections, streams, etc.
Connected = false;
swSender.Close();
srReceiver.Close();
tcpServer.Close();
}
}
private void InitializeConnection()
{
// Parse the IP address from the TextBox into an IPAddress object
ipAddr = IPAddress.Parse(Connect.IpBox.Text);
// Start a new TCP connections to the chat server
tcpServer = new TcpClient();
tcpServer.Connect(ipAddr, int.Parse(Connect.PortBox.Text));
// Helps us track whether we're connected or not
Connected = true;
// Prepare the form
UserName = Connect.NameBox.Text;
// Disable and enable the appropriate fields
IpBox.Enabled = false;
NameBox.Enabled = false;
Main.TxtMsg.Enabled = true;
Connect.BtnConnect.Text = "Disconnect";
// Send the desired username to the server
swSender = new StreamWriter(tcpServer.GetStream());
swSender.WriteLine(UserName);
swSender.Flush();
// Start the thread for receiving messages and further communication
thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
thrMessaging.Start();
}
private void ReceiveMessages()
{
// Receive the response from the server
srReceiver = new StreamReader(tcpServer.GetStream());
// If the first character of the response is 1, connection was successful
string ConResponse = srReceiver.ReadLine();
// If the first character is a 1, connection was successful
if (ConResponse[0] == '1')
{
// Update the form to tell it we are now connected
this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" });
}
else // If the first character is not a 1 (probably a 0), the connection was unsuccessful
{
string Reason = "Not Connected: ";
// Extract the reason out of the response message. The reason starts at the 3rd character
Reason += ConResponse.Substring(2, ConResponse.Length - 2);
// Update the form with the reason why we couldn't connect
this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason });
// Exit the method
return;
}
// While we are successfully connected, read incoming lines from the server
while (Connected)
{
// Show the messages in the log TextBox
this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });
}
}
internal void CloseConnection(string Reason)
{
// Show the reason why the connection is ending
Main.ChatLog.AppendText(Reason + "\r\n");
// Enable and disable the appropriate controls on the form
IpBox.Enabled = true;
NameBox.Enabled = true;
Main.TxtMsg.Enabled = false;
BtnConnect.Text = "Connect";
// Close the objects
Connected = false;
swSender.Close();
srReceiver.Close();
tcpServer.Close();
}
// This method is called from a different thread in order to update the log TextBox
private void UpdateLog(string strMessage)
{
// Append text also scrolls the TextBox to the bottom each time
Main.ChatLog.AppendText(strMessage + "\r\n");
}
}
}
there is another form called Main where all the chat dialog is sent, but the majority of its code is not relevant.
if anyone knows a better way to handle this or knows of a good chat program tutorial i can go through for better examples of how clients connect and disconnect is handled properly without crashing I would be much appreciative.
You should probably think about using Asynchronous programming, where there are no blocking calls.
The problem is, as you surely know, that you close your client while there is an active call.
I'm pretty sure NetworkStream and StreamReader/Writer does have some asynchronous methods. Try to look at some here:
http://msdn.microsoft.com/en-us/library/system.io.streamreader.readasync.aspx
I believe you need to close and dispose each stream per use in your case because you are performing them synchronously. Consider using the using statement for writing...and do something similar for reading. Plus don't forget to remove them from the CloseConnection...
using (NetworkStream ns=tcpServer.GetStream())
{
swSender = new StreamWriter(ns);
swSender.WriteLine(UserName);
swSender.Flush();
ns.Close();
ns.Dispose();
swSender = null;
}
Related
I attached two of the following scripts to a single GameObject.
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class TestScript : MonoBehaviour
{
static UdpClient udp;
private string udp_message = "";
Thread thread;
public int port = 8888;
void Start()
{
udp = new UdpClient(port);
thread = new Thread(new ThreadStart(ThreadMethod));
thread.Start();
}
// Update is called once per frame
void Update()
{
string port_string = port.ToString();
Debug.Log(port_string + ":" + udp_message);
}
void ThreadMethod()
{
while (true)
{
try
{
IPEndPoint remoteEP = null;
byte[] data = udp.Receive(ref remoteEP);
udp_message = Encoding.ASCII.GetString(data);
}
catch
{
}
}
}
}
Then, I used 8888 as the port for one script, as shown in the following image. The other script used port 8889.
I am using two different ports on 8888 and 8889.
However, when I send data to only the 8888 port, the UDP on the 8889 port also seems to be responding.
What is the cause of this?
How can I fix it?
Well your udp field is static!
Both instances of your component overwrite it with different references in
udp = new UdpClient(port);
So basically whichever script runs last "wins" and you are only ever using that UdpClient instance.
Later both your threads will access the very same udp reference for doing
udp.Receive(...);
Simply do not make you field static and you should be fine ^^
private UdpClient udp;
And to your empty catch block ... I would at least make it
catch(Exception e)
{
Debug.LogException(e);
}
which displays an error in the console but doesn't interrupt the thread.
And then also make sure to clean up!
private void OnDestroy()
{
thread?.Abort();
udp?.Dispose();
}
otherwise you might end up with zomby threads or blocked UDP ports ;)
I am working on HoloLens (Unity-UWP) and trying to make a connection with PC (UWP) or Android phone work (Xamarin). So far I tried client and host with both Bluetooth and TCP (even two versions with different libraries) on Android and UWP. I kept the code entirely separated from user interface, so that it is easier to use, to understand and modular. An Action<string> is used to output results (error logs and sent messages).
Everything that is not on the HoloLens works fine (even though it's exactly the same code). It worked from PC (UWP) to Android with client and host switched. But it doesn't even work between HoloLens and PC (UWP). The behavior ranged from crashes (mostly for Bluetooth) to instant disconnection. The last tests resulted in disconnection once bytes are about to be received. It could even read the first 4 bytes (uint for the length of the following UTF-8 message), but then it was disconnected. The other devices seemed to work fine.
What I know: Capabilities are set, the code works, the issue is likely something that is common for everything that has to do with networking and HoloLens.
So the question is, is Unity or HoloLens incompatible with something I am using? What I used which is worth mentioning: StreamSocket, BinaryWriter, BinaryReader, Task (async, await). Or is HoloLens actively blocking communication with applications on other devices? I know it can connect to devices with Bluetooth and that it can connect via TCP, and it looks like people succeed to get it to work. Are there known issues? Or is there something with Unity that causes this - a bad setting maybe? Do I have to use async methods or only sync? Are there incompatibility issues with Tasks/Threads and Unity? Is this possibly the issue (inability to consent to permissions)?
Another thing to note is that I cannot ping HoloLens via its IP by using the cmd, even though the IP is correct.
I'd appreciate any advice, answer or guess. I can provide more information if requested (see also the comments below). I would suggest to focus on the TCP connection as it seemed to be working better and appears to be more "basic." Here is the code:
using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Networking;
using Windows.Networking.Sockets;
#region Common
public abstract class TcpCore
{
protected StreamSocket Socket;
protected BinaryWriter BWriter;
protected BinaryReader BReader;
protected Task ReadingTask;
public bool DetailedInfos { get; set; } = false;
public bool Listening { get; protected set; }
public ActionSingle<string> MessageOutput { get; protected set; } = new ActionSingle<string> (); // Used for message and debug output. They wrap an Action and allow safer use.
public ActionSingle<string> LogOutput { get; protected set; } = new ActionSingle<string> ();
protected const string USED_PORT = "1337";
protected readonly Encoding USED_ENCODING = Encoding.UTF8;
public abstract void Disconnect ();
protected void StartCommunication ()
{
Stream streamOut = Socket.OutputStream.AsStreamForWrite ();
Stream streamIn = Socket.InputStream.AsStreamForRead ();
BWriter = new BinaryWriter (streamOut); //{ AutoFlush = true };
BReader = new BinaryReader (streamIn);
LogOutput.Trigger ("Connection established.");
ReadingTask = new Task (() => StartReading ());
ReadingTask.Start ();
}
public void SendMessage (string message)
{
// There's no need to send a zero length message.
if (string.IsNullOrEmpty (message)) return;
// Make sure that the connection is still up and there is a message to send.
if (Socket == null || BWriter == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; }
uint length = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (length);
byte[] buffer = USED_ENCODING.GetBytes (message);
if (DetailedInfos) LogOutput.Trigger ("Sending: " + message);
BWriter.Write (countBuffer);
BWriter.Write (buffer);
BWriter.Flush ();
}
protected void StartReading ()
{
if (DetailedInfos) LogOutput.Trigger ("Starting to listen for input.");
Listening = true;
while (Listening)
{
try
{
if (DetailedInfos) LogOutput.Trigger ("Starting a listen iteration.");
// Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message (=n).
uint length = BReader.ReadUInt32 ();
if (DetailedInfos) LogOutput.Trigger ("ReadLength: " + length.ToString ());
MessageOutput.Trigger ("A");
byte[] messageBuffer = BReader.ReadBytes ((int) length);
MessageOutput.Trigger ("B");
string message = USED_ENCODING.GetString (messageBuffer);
MessageOutput.Trigger ("Received Message: " + message);
}
catch (Exception e)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
{
// Seems to occur on disconnects. Let's not throw().
Listening = false;
Disconnect ();
LogOutput.Trigger ("Unknown error occurred: " + e.Message);
break;
}
else
{
Listening = false;
Disconnect ();
break;
}
}
}
LogOutput.Trigger ("Stopped to listen for input.");
}
}
#endregion
#region Client
public class GTcpClient : TcpCore
{
public async void Connect (string target, string port = USED_PORT) // Target is IP address.
{
try
{
Socket = new StreamSocket ();
HostName serverHost = new HostName (target);
await Socket.ConnectAsync (serverHost, port);
LogOutput.Trigger ("Connection successful to: " + target + ":" + port);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
#region Server
public class GTcpServer : TcpCore
{
private StreamSocketListener socketListener;
public bool AutoResponse { get; set; } = false;
public async void StartServer ()
{
try
{
//Create a StreamSocketListener to start listening for TCP connections.
socketListener = new StreamSocketListener ();
//Hook up an event handler to call when connections are received.
socketListener.ConnectionReceived += ConnectionReceived;
//Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await socketListener.BindServiceNameAsync (USED_PORT);
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
private void ConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
listener.Dispose ();
Socket = args.Socket;
if (DetailedInfos) LogOutput.Trigger ("Connection received from: " + Socket.Information.RemoteAddress + ":" + Socket.Information.RemotePort);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection Received error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
Coincidentially, I just implemented a BT connection between HoloLens and an UWP app. I followed the sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothRfcommChat.
As capabilities, I set "Bluetooth" (of course), "Internet (Client & Server)" and "Private Networks (Client & Server)". The steps on the server side then are:
Create an RfcommServiceProvider for your own or an existing (eg OBEX object push) service ID.
Create a StreamSocketListener and wire its ConnectionReceived Event.
Bind the service Name on the listener: listener.BindServiceNameAsync(provider.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
If you have a custom service ID, set its name along with other attributes you may want to configure. See the sample linked above for this. I think, this is mostly optional.
Start advertising the BT service: provider.StartAdvertising(listener, true);
Once a client connects, there is a StreamSocket in the StreamSocketListenerConnectionReceivedEventArgs that you can use to create a DataReader and DataWriter on like on any other stream. If you only want to allow one client, you can also stop advertising now.
On the client side, you would:
Show the DevicePicker and let the user select the peer device. Do not forget setting a filter like picker.Filter.SupportedDeviceSelectors.Add(BluetoothDevice.GetDeviceSelectorFromPairingState(true)); You can also allow unpaired devices, but you need to call PairAsync before you can continue in step 2. Also, I think there is no way to circumvent the user consent dialogue in this case, so I would recommend pairing before. To be honest, I did not check whether the unpaired stuff works on HoloLens.
You get a DeviceInformation instance from the picker, which you can use to obtain a BT device like await BluetoothDevice.FromIdAsync(info.Id);
Get the services from the device like device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); and select the one you are interested in. Note that I found that the built-in filtering did not behave as expected, so I just enumerated the result and compared the UUIDs manually. I believe that the UWP implementation performs a case-sensitive string comparison at some point, which might lead to the requested service not showing up although it is there.
Once you found your service, which I will call s from now on, create a StreamSocket and connect using socket.ConnectAsync(s.ConnectionHostName, s.ConnectionServiceName, SocketProtectionLevel.PlainSocket);
Again, you can not create the stream readers and writers like on the server side.
The answer is Threading.
For whoever may have similar issues, I found the solution. It was due to Unity itself, not HoloLens specifically. My issue was that I wrote my code separately in an own class instead of commingle it with UI code, which would have made it 1. unreadable and 2. not modular to use. So I tried a better coding approach (in my opinion). Everybody could download it and easily integrate it and have basic code for text messaging. While this was no issue for Xamarin and UWP, it was an issue for Unity itself (and there the Unity-UWP solution as well).
The receiving end of Bluetooth and TCP seemed to create an own thread (maybe even something else, but I didn't do it actively), which was unable to write on the main thread in Unity, which solely handles GameObjects (like the output log). Thus I got weird log outputs when I tested it on HoloLens.
I created a new TCP code which works for Unity but not the Unity-UWP solution, using TcpClient/TcpListener, in order to try another version with TCP connection. Luckily when I ran that in the editor it finally pointed on the issue itself: The main thread could not be accessed, which would have written into the UI - the text box for the log output. In order to solve that, I just had to use Unity's Update() method in order to set the text to the output. The variables themselves still could be accessed, but not the GameObjects.
So what I am specifically trying to do is get a serial proxy going to pipe an arduino into Unity3d to run on linux. System.IO.Ports just doesn't work on linux with unity.
So I have gotten a python based serial proxy script, I have that up and running just fine. I can netcat into that localhost and actually get output from the arduino.
nc localhost 8082
g'day from bitty 1.0 -- type 'logout' to disconnect
Connecting to /dev/tty.usbmodem5d11... connected.
HELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLO
I have the arduino only printing out HELLO on repeat.
But the serial proxy is up and running, and has data going over it.
Now I am trying to write the code in Unity to receive this data, and I just can't get it to work.
This is my code for that:
public Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
public byte[] buffer = new byte[256];
int pinRead = 0;
void Start ()
{
socket.Connect(IPAddress.Parse("127.0.0.1"),8082);
}
void Update ()
{
if (socket.IsBound)
{
try
{
int bytesRead;
bytesRead = socket.Receive(buffer);
string incommingdata = System.Text.Encoding.ASCII.GetString(buffer);
Debug.Log(bytesRead+" || "+incommingdata);
}
catch (System.Exception e)
{
}
}
bytesRead is ALWAYS 0 and incomingData just doesn't have anything. It connects, and isBound returns true. I just can't get the code right to receive the data and put it in a format that is usable.
Please help. I need to get this working and its far out of my area of expertise. How do I make this work?
So I got this to work using tcpclient objects and some snippets of code that were posted on a random unity blog....
Here it is if anyone wants to see. I would still be really curious to know how to get the Socket implementation functioning though.
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System;
using System.Text;
using System.IO;
using System.Threading;
public class ArduinoTest : MonoBehaviour
{
private bool socketReady = false;
private TcpClient mySocket;
private NetworkStream theStream;
private StreamWriter theWriter;
private StreamReader theReader;
private string Host = "localhost";
private Int32 Port = 9900;
private int pinRead = 0;
void Start ()
{
setupSocket();
}
void Update ()
{
string read = readSocket();
if (read != "")
{
int value = int.Parse(read);
if ( value > 53)
{
Debug.Log((value-54)+" DOWN");
}
else
{
Debug.Log(value+" UP");
}
}
}
void OnApplicationQuit()
{
writeSocket("logout");
closeSocket();
}
public void setupSocket()
{
try
{
mySocket = new TcpClient(Host, Port);
theStream = mySocket.GetStream();
theWriter = new StreamWriter(theStream);
theReader = new StreamReader(theStream);
socketReady = true;
}
catch (Exception e)
{
Debug.Log("Socket error:" + e);
}
}
public void writeSocket(string theLine)
{
if (!socketReady)
return;
String tmpString = theLine + "\r\n";
theWriter.Write(tmpString);
theWriter.Flush();
}
public string readSocket()
{
if (!socketReady)
return "";
if (theStream.DataAvailable)
return theReader.Read().ToString();
return "";
}
public void closeSocket()
{
if (!socketReady)
return;
theWriter.Close();
theReader.Close();
mySocket.Close();
socketReady = false;
}
public void maintainConnection()
{
if(!theStream.CanRead)
{
setupSocket();
}
}
}
(Sorry I can't post comments yet or would do so there)
BytesRead = 0 typically means the remote side disconnected.
For what it's worth, your code works fine when I run a test TCP server listening for connections on port 8082 and sending out some text messages. So I doubt the problem is on the C# client-side.
Beyond this simple example, "getting this to work" is not necessarily a simple question. Here are a couple things you should keep in mind:
You are making a blocking Receive call, which means you will need to call Update in a loop.
TCP is a streaming protocol, so you might get partial messages or many messages bundled together in the same Read call. Look up "TCP Framing" to get more details on this.
You can try using telnet on this socket, just to see if you can connect to it.
telnet 127.0.0.1 8082
I am creating a chat client in C# to be demonstrated on localhost.
Here's the relevant code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
static List<TcpListener> garabage_collection_preventor = new List<TcpListener>();
static Dictionary<IPEndPoint, bool> address_dictionary = new Dictionary<IPEndPoint, bool>();
static int port_increment = 9999;
static int client_id = 0;
void start_listening()
{
while (true)
{
TcpListener listen = new TcpListener(IPAddress.Any, port_increment);
garabage_collection_preventor.Add(listen);
listen.Start();
TcpClient client = listen.AcceptTcpClient();
IPEndPoint temp_end = ((IPEndPoint)listen.LocalEndpoint);
address_dictionary.Add(temp_end, false);
port_increment++;
new Thread(new ParameterizedThreadStart(connection_stuff)).Start(client);
}
}
void writer(object ob,int end_point)
{
StreamWriter write = ob as StreamWriter;
while (true)
{
foreach (KeyValuePair<IPEndPoint, bool> value in address_dictionary)
{
IPEndPoint index = value.Key;
int temp = value.Key.Port;
if (temp == end_point)
{
while (address_dictionary[index] == true)
{
write.WriteLine(msg_box.Text);
}
}
}
}
}
void reader(StreamReader read)
{
while (true)
{
MessageBox.Show(read.ReadLine());
}
}
void connection_stuff(object ob)
{
TcpClient client = ob as TcpClient;
int writer_identification_endpoint = ((IPEndPoint)client.Client.LocalEndPoint).Port;
NetworkStream stream = client.GetStream();
StreamReader read = new StreamReader(stream);
StreamWriter write = new StreamWriter(stream);
ThreadStart port_passing = delegate { writer(write, writer_identification_endpoint); };
Thread thread = new Thread(port_passing);
reader(read);
thread.Start();
}
public Form1()
{
InitializeComponent();
}
private void send_Click(object sender, EventArgs e)
{
int end_point = int.Parse(port.Text);
foreach (KeyValuePair<IPEndPoint, bool> value in address_dictionary)
{
IPEndPoint index = value.Key;
int temp = value.Key.Port;
if (temp == end_point)
{
address_dictionary[index] = true;
}
}
}
private void listener_Click(object sender, EventArgs e)
{
new Thread(start_listening).Start();
listener.Enabled = false;
}
}
}
Now the problem is that the first client can easily connect with the program and send messages which this program can easily read. However every subsequent client cannot connect.
I know that I shouldn't be making creating than one TCPListener but the problem is that I have to demonstrate the program on localhost and port number is the only true way to differentiate between client.
So please tell me what's wrong with the code I have been banging my head against the wall on it for hours.
EDIT
this is what happens when english is not a first language :) This code(not complete presently) will be a chat client. Each instance of this code will be able to connect with other instance of this same code to communicate. Any number of instances should be able to connect with any number of instances(like if double click 5 times the program there would now be 5 instances ready to communicate with each other).
Now the problem is that every instance will have the same IP Address(Because they are all running on the same machine). The question arises how say instance 1 suppose to connect to instance 4, ip can't be used here because instance 2,3 and 5 will also have the same IP address. So what i am trying to do is to connect instance 1 with instance 4 with IP Address and PORT instead of just using just the IP Address as is the case with a single TCPListener.
Try moving these three lines of code outside the while(true) loop in your start_listening routine.
TcpListener listen = new TcpListener(IPAddress.Any, port_increment);
garabage_collection_preventor.Add(listen);
listen.Start();
You only need one listener, from which you accept many different connections.
Try it like this:
void start_listening()
{
TcpListener listen = new TcpListener(IPAddress.Any, port_increment);
garabage_collection_preventor.Add(listen);
listen.Start();
while (true)
{
TcpClient client = listen.AcceptTcpClient();
// etc
}
}
That is, once you have created your listener, it runs in the loop accepting incoming connections from the client.
Edit: The problem you are having is because your simulation is flawed. In the real world, each chat server runs in a single O/S instance, on one, known, port number. Because you only have one O/S instance yourself, you can't run multiple chat servers on that instance all listening on the same port. But something like this might work, i.e., you need two loops, the first one to create your listeners, and the second inner loop for each listener to accept multiple clients. Note: these loops need exit conditions!
void start_listening()
{
while (true)
{
TcpListener listen = new TcpListener(IPAddress.Any, port_increment);
garabage_collection_preventor.Add(listen);
listen.Start();
while (true)
{
TcpClient client = listen.AcceptTcpClient();
IPEndPoint temp_end = ((IPEndPoint)listen.LocalEndpoint);
address_dictionary.Add(temp_end, false);
new Thread(new ParameterizedThreadStart(connection_stuff)).Start(client);
}
port_increment++;
}
}
I am getting into trouble with this part of code.
In fact I want to set a Client/Server Application.
In the client part I launch a Thread which function is only to check everytime if it is connected to the server (if the connection to the server is still established)
TraceLog is a class that uses its Info() method to write in a file.
this is is the client code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace ClientApp
{
class ClientOpenConnection
{
private static Thread threadConnect;
static TcpClient myClient = new TcpClient();
static String host = "";
static Int32 port = 0;
//Function that makes the client runs
public static void RunClient(String hostname, Int32 hostport)
{
host = hostname;
port = hostport;
int _tryAgain = 0;
while (!myClient.Connected) {
try
{ //I start the connection
myClient.Connect(host, port);
}
catch {
}
_tryAgain += 10;
if (_tryAgain == 1000)
break;
//_tryAgain allows me to define how long will the client try to connect to the server.
}
TraceLog.Info("Out of the while ", ""); // This is to know where am I
if (_tryAgain != 1000)
{ //If I get out because _tryAgain is less than 1000. It means that I am already connected to the server
//Here I start a Thread to be sure that I am always connected to the server
threadConnect = new Thread(isConnected);
threadConnect.Start();
TraceLog.Info("Launch the thread","");
}
//While threadConnect is executing parallely I continue my program
}
private static void isConnected() {
//I keep my eyes on the network connection
while (myClient.Connected) {
//Nothing is done
}
TraceLog.Info("The connection has been lost","");
RunClient(host,port);
}
}
}
The problem that I am having, when I start the client before the server I enter the first WHILE loop. it is OK at this level.
and when I start the server after, I launch the threadConnect but the problem is that if now I stop the server, normally i should have inside the log file "The connection has been lost" but I have nothing.
What is wrong with this part of code?
Have you already done something like this in the past?
I come with a modification but still having problem to obtain what I want, ie the client still get trying to contact the server eveytime even if the server is stopped .
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace ClientApp
{
class ClientOpenConnection
{
private static Thread threadConnect;
static TcpClient myClient = new TcpClient();
static String host = "";
static Int32 port = 0;
//Function that makes the client runs
public static void RunClient(String hostname, Int32 hostport)
{
host = hostname;
port = hostport;
TraceLog.Info(" -> "+myClient.Connected,"");
while (!myClient.Connected) {
try
{
myClient.Connect(host, port);
TraceLog.Info(" <-> " + myClient.Connected, "");
}
catch {
TraceLog.Info("Trying to contact the server","");
}
}
TraceLog.Info("I am connected ", "");
//Here I start a Thread to be sure that I am always connected to the server
threadConnect = new Thread(isConnected);
threadConnect.Start();
TraceLog.Info("Launch the thread to be sure I am constantly online","");
}
private static void isConnected() {
//I keep my eyes on the network connection
TraceLog.Info("->>"+myClient.Connected,"");
while (myClient.Connected) {
Thread.Sleep(500);
try
{
NetworkStream stream = myClient.GetStream();
ASCIIEncoding ascii = new ASCIIEncoding();
byte[] _incomingMsg = new byte[1024];
stream.Read(_incomingMsg, 0, _incomingMsg.Length);
String strToGet = System.Text.Encoding.ASCII.GetString(_incomingMsg);
strToGet = strToGet.Trim();
if (!strToGet.Equals("ONLINE"))
if (strToGet.Equals(""))
{
TraceLog.Info("The message receive is empty","");
break;
}
}
catch {
break;
}
}
TraceLog.Info("The connection has been lost", "");
RunClient(host, port);
}
}
}
But when I call the RunClient() in the isConnected() function it executes in the WHILE and output TraceLog.Info("Trying to contact the server",""); even if I start the server again, the client remains in the while loop and never connects at all.
From MSDN:
The Connected property gets the connection state of the Client socket
as of the last I/O operation. When it returns false, the Client socket
was either never connected, or is no longer connected.
Because the Connected property only reflects the state of the
connection as of the most recent operation, you should attempt to send
or receive a message to determine the current state. After the message
send fails, this property no longer returns true. Note that this
behavior is by design. You cannot reliably test the state of the
connection because, in the time between the test and a send/receive,
the connection could have been lost. Your code should assume the
socket is connected, and gracefully handle failed transmissions.
In other words, in order to check if you are still connected, you need to send or receive some data and then check the connection state.
Since your code doesn't send any packets after the connection is made, the connected property always returns true, and the loop never exits.