I am rather new to C# and Sockets however I am trying my hand at making a kind of "chat" program. What I am having an issue with at the moment is making the client able to automatically receive a message from the server if one is available.
What I have tried so far:
System.Threading.Thread myThread = new System.Threading.Thread(new
System.Threading.ThreadStart(listenThread));
public static void listenThread()
{
while(true){
Form1 form1 = new Form1();
form1.ReceiveLoop();
}
}
private void ReceiveLoop()
{
clientSocket.ReceiveTimeout = 100;
byte[] receivedBuffer = new byte[1024];
try
{
int rec = clientSocket.Receive(receivedBuffer);
byte[] data = new byte[rec];
Array.Copy(receivedBuffer, data, rec);
updateClient("Received: " + Encoding.ASCII.GetString(data));
}
catch (SocketException e)
{
//MessageBox.Show(e.ToString());
}
}
ReceiveLoop() works when I access from the GUI thread, however it will not work(Throws SocketException, due to timeout) when accessed via myThread.
Any help or suggestions on another way I could approach this would be greatly appreciated :D
Related
In my HoloLens2 application sometimes the UDP-Package-receive rate drops instantly from 40 packages per second to 0-2 packages per second and stays there (size of packages between 2000 byte and 60000 byte. The application is made with Unity3d and .Net Sockets for networking. This behaviour sometimes happens straight from application startup, sometimes after 30 minutes, so basically quiet random. Even Restarting HoloLens2 doesn´t help in such a situation. The application logs no exception or error messages. On my Unity-Editor the Application can handle the network traffic without any problem.
It feels like some Network buffer got stuffend and drops all packages so did I miss some Cleanup command in my Networking-Code or is there another way to face Receive-Overload in my code?
Or is HoloLens2 somehow limited in it´s Bandwith when running via Ethernet (and not Wifi)?
At the same time my application also maintains a peer-to-peer Video-stream (WebRTC with MixedReality Capture). This stream works well over the whole lifetime of the application flawlessly.
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class UdpCommunicationDotNet : ICommunicator
{
#region Fields
private int m_PackageCountPerSecond;
private int m_PackageCountTotal;
private Thread m_PackageCounterResetRoutine;
private Socket m_SenderSocket;
private Socket m_ReceiverSocket;
private IPEndPoint m_ServerIpEndPoint;
private IPEndPoint m_ReceiverIpEndPoint;
private IPEndPoint m_ListenerIpEndPoint;
private EndPoint m_ServerEndPoint;
private EndPoint m_ReceiverEndPoint;
private byte[] m_ReceiveBuffer;
private IDataProvider m_DataProvider;
private int m_MaxBufferByteLength;
private bool m_ReceiveEnabled = true;
public IDataProvider DataProvider { get => m_DataProvider; set => m_DataProvider = value; }
#endregion
#region Constructor
public UdpCommunicationDotNet()
{
m_MaxBufferByteLength = GameManager.Instance.GetConfigurationManager.RuntimeConfig.NetworkSettings.MaxBufferByteLength;
}
#endregion
#region Public Methods
public void Initialize(DataLineConfig dataLineConfig)
{
m_ReceiveBuffer = new byte[m_MaxBufferByteLength];
m_ServerIpEndPoint = dataLineConfig.serverEndPoint;
m_ReceiverIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
m_ServerEndPoint = (EndPoint)m_ServerIpEndPoint;
m_ReceiverEndPoint = (EndPoint)m_ReceiverIpEndPoint;
m_ListenerIpEndPoint = dataLineConfig.receiverEndPoint;
}
public void StartReceiver()
{
if (InitializeReceiverSocket())
{
m_ReceiveEnabled = true;
StartReceiving();
}
}
public void EndReceiver()
{
m_ReceiveEnabled = false;
}
public void StartSender()
{
InitializeSenderSocket();
}
public void Send(Byte[] data)
{
try
{
//added this, remove it
//Debug.Log("SentMyDataPackage");
m_SenderSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, m_ServerEndPoint, new AsyncCallback(OnSendCallback), null);
}
catch (Exception e)
{
Debug.LogError("Failed to send udp data through the UdpCommunicator: " + e.Message);
}
}
#endregion
#region Private Methods
private bool InitializeReceiverSocket()
{
bool isInitialized = false;
try
{
m_ReceiverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_ReceiverSocket.Bind(m_ListenerIpEndPoint);
isInitialized = true;
}
catch (Exception e)
{
Debug.LogError("Failed to initialize the receiver socket in the UdpCommunicator: " + e.Message);
}
return isInitialized;
}
private void InitializeSenderSocket()
{
m_SenderSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
private void StartReceiving()
{
m_ReceiverSocket.BeginReceiveFrom(m_ReceiveBuffer, 0, m_ReceiveBuffer.Length, SocketFlags.None, ref m_ReceiverEndPoint, new AsyncCallback(OnReceiveCallback), m_ReceiverEndPoint);
}
private void DistributeData(object dataObject)
{
byte[] data = (byte[])dataObject;
m_DataProvider.DistributeData(data);
}
#endregion
#region Callback Methods
private void OnSendCallback(IAsyncResult asyncResult)
{
try
{
m_SenderSocket.EndSend(asyncResult);
}
catch (Exception e)
{
Debug.LogError("Failed to close the opened send connection in the UdpCommunicator: " + e.Message);
}
}
private void OnReceiveCallback(IAsyncResult asyncResult)
{
try
{
if (DebugManager.Instance.isActive)
{
DebugManager.Instance.m_PackageCounter.UpdateMainCountTotal(m_PackageCountTotal);
}
int receivedBufferSize = m_ReceiverSocket.EndReceiveFrom(asyncResult, ref m_ReceiverEndPoint);
Array.Resize(ref m_ReceiveBuffer, receivedBufferSize);
byte[] copiedBuffer = new byte[receivedBufferSize];
Buffer.BlockCopy(m_ReceiveBuffer, 0, copiedBuffer, 0, receivedBufferSize);
Thread thread = new Thread(DistributeData);
thread.SetApartmentState(ApartmentState.STA);
thread.Start(copiedBuffer);
m_ReceiveBuffer = new byte[m_MaxBufferByteLength];
}
catch (Exception e)
{
Debug.LogError("Failed to fetch the received data in the UdpCommunicator: " + e.Message);
}
finally
{
if (m_ReceiveEnabled)
{
StartReceiving();
}
else
{
m_ReceiverSocket.Shutdown(SocketShutdown.Both);
m_SenderSocket.Shutdown(SocketShutdown.Both);
m_ReceiverSocket.Close();
m_SenderSocket.Close();
}
}
}
#endregion
}
Edit:
By writing the Minimal Reproducable Example I found out the reason for the missbehaviour! I realized that if I send more than 4 Packages in one chunk (so send package1, send package2, send package3, send package4, etc, wait x ms, start again) HoloLens2 is starting to drop my udp packages.
In contrast, when I send Package1-> wait for 4 ms -> send package2 -> wait for 4 ms -> send package 3 -> wait 4ms and so on and after sending all different packages simply restart sending, it Works like a charm!
Also, when I send the packages via EthernetOverUSB (what is not a usable approach for my case), even if I send all packages in one chunk it works.
For me this behaviour is wiered and I can´t explain it to myself. Maybe somebody of you can ? Anyways, thanks for anybody who tried to help me!
I am currently developing a C# TCP Server application for my game. The Server functions perfectly for a long period of time, closes however unexpectedly after about a day or 2 of running. I've furthermore printed the console output to a file, which I checked after the application closed, and there were no errors, or at least none were printed. The Code for the server is the following :
class Program
{
private static Thread consoleThread;
static void Main(string[] args)
{
//To get the output in a file
FileStream filestream = new FileStream("out.txt", FileMode.Create);
var streamwriter = new StreamWriter(filestream);
streamwriter.AutoFlush = true;
Console.SetOut(streamwriter);
Console.SetError(streamwriter);
InitializeConsoleThread();
ServerHandleData.InitializePacketListener();
ServerTCP.InitializeServer();
}
private static void InitializeConsoleThread()
{
consoleThread = new Thread(ConsoleLoop);
consoleThread.Name = "ConsoleThread";
consoleThread.Start();
}
private static void ConsoleLoop()
{
while (true)
{
}
}
}
The TCP Clients are handled with the following async methods :
private static void InitializeServerSocket()
{
ServerSocket = new TcpListener(IPAddress.Any, 8080);
ServerSocket.Start();
ServerSocket.BeginAcceptTcpClient(ClientConnectCallback, null); //Once we get a connection, ClientConnectCallback is called
}
private static void ClientConnectCallback(IAsyncResult result)
{
TcpClient tempClient = ServerSocket.EndAcceptTcpClient(result); //Accepts the connection and creates its corresponding TcpClient
ServerSocket.BeginAcceptTcpClient(ClientConnectCallback, null); //Open the connection again for other players
string[] IPs = tempClient.Client.RemoteEndPoint.ToString().Split(':');
if(IPDictionary.TryGetValue(IPs[0], out ClientObject C))
{
C.Socket = tempClient;
C.Socket.NoDelay = true;
C.Socket.ReceiveBufferSize = 4096;
C.Socket.SendBufferSize = 4096;
C.ReceiveBuffer = new byte[4096];
C.myStream = C.Socket.GetStream();
C.IP = tempClient.Client.RemoteEndPoint.ToString();
C.myStream.BeginRead(C.ReceiveBuffer, 0, C.Socket.ReceiveBufferSize, C.ReceiveCallback, null);
PACKET_SendMessage(C.ConnectionID, "CheckIP", 0, IPs[0]);
Console.WriteLine("Connection incoming from {0}", C.IP);
return;
}
Random ran = new Random();
int curID = ran.Next(); //random signed int
while (curID == 0 || ClientObjects.ContainsKey(curID)) { curID = ran.Next(); }
ClientObjects[curID] = new ClientObject(tempClient, curID);
TempPlayers[curID] = new TempPlayer();
}
which are called in the Program class with the initializeServer methods.
The Server functions well for a while and then suddenly closes, seemingly without any reason. The only cause I could find while searching on the internet is that the program eventually runs out of memory and just closes. Wouldn't however the garbage collector integrated in C# prevent such a thing ? Thank you all for your help in advance, I hope I made the question as direct and as clear as possible. Furthermore, if there are any other ways to improve these snippets of code, please do tell.
I'm creating a game in which I use TCP/IP connection. The problem is that I'm using .Invoke to help me receive and send message.
The program goes like this: I'm my first window, i'm starting and connecting to the server like this :
{
TcpListener listener = new TcpListener(IPAddress.Any, this.port);
listener.Start();
try {
this.client = listener.AcceptTcpClient();
gameWindow = new GameWindow(this.client, true);
gameWindow.StartGame();
}
}
then i'm connecting to it like this:
{
IPEndPoint ipEnd = new IPEndPoint(this.serverIP, this.port);
{
try {
client.Connect(ipEnd);
if (client.Connected) {
gameWindow = new GameWindow(this.client, false);
gameWindow.StartGame();
}
}
}
The constructor for gameWindow (which is a form) looks like this:
public GameWindow(TcpClient thisClient, bool isServer)
{
InitializeComponent();
this.client = thisClient;
this.reader = new StreamReader(thisClient.GetStream());
this.writer = new StreamWriter(thisClient.GetStream());
this.writer.AutoFlush = true;
}
I must wait for the server to send a message to the client, and then start the client ( I have a function .startGame() that uses .ShowDialog() and creates some pictureBoxs)
But nowhere I can get my handle created. I've tried to put this.createHandle() (read about it here) into GameWindow_Load but still not works. If I try to send a message with:
workerSendData.RunWorkerAsync(); I get:
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
What can I do to get my handler created? Using Thread.Sleep will sleep my whole UI, which does not work (a "solution" found on the internet)
My code for sending message :
private void workerSendData_DoWork(object sender, DoWorkEventArgs e)
{
if (client.Connected) {
this.writer.WriteLine(this.toSend); // aici trimitem datele.
// de modificat : aici vom adauga in lista noastra miscarile.
this.Invoke(new MethodInvoker(delegate () { MessageBox.Show("Me:" + this.toSend + "\n"); }));
}
else {
MessageBox.Show("Send failed");
}
workerSendData.CancelAsync();
}
My code for receiving data:
private void workerReceiveData_DoWork(object sender, DoWorkEventArgs e)
{
while (client.Connected) {
try {
this.received = this.reader.ReadLine();
this.myTurn = true;
this.Invoke(new MethodInvoker(delegate () {
MessageBox.Show("This has been received: " + this.received);
/*this.tbReceive.AppendText("You:" + this.received + "\n");*/
}));
this.received = "";
}
catch (Exception x) {
MessageBox.Show(x.Message.ToString());
}
}
}
It seems that you cannot invoke an action before the Window is fully initialized and loaded. Assuming you are working in Windows Forms, there is a solution provided by #Greg D on this question, but it doesn't be to be the safest way to go.
I would suggest that you try to find a way to start the worker only after the window is loaded (for example using the Loaded event), so that the handle is definitely ready and this situation does not occur.
i do have an embedded board (mini/tiny 210)
and i did try building an application which uses system.socket and system.socket.net in C# ( vs2005 - wince5 device application but i do have wince 6 on my device!! )
my purpose is to send and receive data bytes using Ethernet. for sending , well there is no problem but receive does need multitasking.
i should start a task so it can listen to the port if there is any data upcoming but once i add thread part to my code the whole program crashes (dose not do that in pc x64 core i5) but in embedded board it will crash.
if i eliminate multi tasking i might lose some data when networkstream.read is not online or when networkstream.read is online and i'm waiting for data,which ends in program will not respond up until the timeout. here is some of my codes . hope it helps.
this code does work :
private void button_connect_Click(object sender, EventArgs e)
{
try
{
//Thread TH = new Thread(new ThreadStart(con));
//TH.Start();
con();
}
catch
{
MessageBox.Show("THREAD ERROR");
}
}
public void con()
{
try
{
cli = null;
cli = new TcpClient();
cli.Connect(IPAddress.Parse(textBox_ip.Text),Convert.ToInt32(textBox_port.Text));
st = null;
st = cli.GetStream();
button_connect.Enabled = false;
button_Close.Enabled = false;
button_disconnect.Enabled = true;
statusBar1.Text = "Connected to server";
//Thread rec = new Thread(new ThreadStart(recf));
//rec.Start();
}
catch
{
MessageBox.Show("Connection faild","Error");
statusBar1.Text = "Faild to connect to server";
}
this code dose not work :
private void button_connect_Click(object sender, EventArgs e)
{
try
{
Thread TH = new Thread(new ThreadStart(con));
TH.Start();
//con();
}
catch
{
MessageBox.Show("THREAD ERROR");
}
}
public void con()
{
try
{
cli = null;
cli = new TcpClient();
cli.Connect(IPAddress.Parse(textBox_ip.Text),Convert.ToInt32(textBox_port.Text));
st = null;
st = cli.GetStream();
button_connect.Enabled = false;
button_Close.Enabled = false;
button_disconnect.Enabled = true;
statusBar1.Text = "Connected to server";
//Thread rec = new Thread(new ThreadStart(recf));
//rec.Start();
}
catch
{
MessageBox.Show("Connection faild","Error");
statusBar1.Text = "Faild to connect to server";
}
}
as you can see i'm getting this crash while just one of my task is in the code the second task has been commented
i appreciate your help in advance
The crash is probably caused by updating UI elements from a secondary thread.
The con method reads a textbox and updates several buttons as well as a status bar, and this should only happen on the main application thread.
To verify whether this is the issue, you could simply comment out the lines in the con method that access UI elements (copy the textbox value to a member string before starting the thread, or just hard-code the IP address) and re-run the application.
Then to actually fix the problem you'll have to look into Control.Invoke.
I've got problem when I am trying to get access to form's controls from another class. My program is hanging in infinite loop. I know why, but I don't know how to write this correctly.
Here is Form1.cs (to my Form)
public Form1()
{
InitializeComponent();
Load config = new Load();
string[] data = config.readConfig("config.ini");
if (data.Length == 4) { //client
Client run = new Client();
run.startClient(data[1], Convert.ToInt32(data[2]));
}
else if (data.Length == 3) //server
{
Server run = new Server();
run.startServer(Convert.ToInt32(data[1]));
}
}
public void addLog(string dataLog){
richTextBox1.Text += dataLog;
}
and here is Client.cs file:
class Client
{
public void startClient(string ipAddr, int port)
{
Form1 form1 = new Form1();
TcpClient client = new TcpClient();
try
{
form1.addLog("Connecting...");
client.Connect(ipAddr, port);
form1.addLog("Connected to server: " + ipAddr + ":" + port.ToString());
}
catch
{
MessageBox.Show("We couldn't connect to server");
}
}
}
How can I change text value without running each time new form. Maybe There is something like run_once?
The infinite loop is here:
Form1:
//Always runs if the config file is a certain length
Client run = new Client();
Client:
Form1 form1 = new Form1();
Each constructor creates the other object, which in turn creates the first object, ad infintum.
If you need to get the form object to the client don't create a new one!. It doesn't work anyways, as your new form object knows nothing about the old one. Just pass it in:
public Client(Form1 form)
{
//Do whatever with it
}
//Form class
Client c = new Client(this);
Disclaimer: There are usually far better ways to do this, but you'll learn those as you get more familiar with design patterns/architecture.