This question already has answers here:
The calling thread cannot access this object because a different thread owns it
(15 answers)
Closed 6 years ago.
When ever I press the send button the TCP Client, it stops the TCP Server and throws me this error.
The calling thread cannot access this object because a different
thread owns it.
I've tried debugging it but couldnt find the issue.
How would I be able to fix this since it is bugging me alot and I am new to TCP / IP and threading aswell.
I know how it works but yeah.
In the listenerThread() method its this line that is throwing me this error.
lbConnections.Items.Add(handlerSocket.RemoteEndPoint.ToString() + " connected.");
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Text;
namespace SimpleTCPServer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ArrayList nSockets;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IPHostEntry IPHost = Dns.GetHostByName(Dns.GetHostName());
lblStatus.Content = "My IP address is " + IPHost.AddressList[0].ToString();
nSockets = new ArrayList();
Thread thdListener = new Thread(new ThreadStart(listenerThread));
thdListener.Start();
}
public void listenerThread()
{
TcpListener tcpListener = new TcpListener(8080);
tcpListener.Start();
while (true)
{
Socket handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
lbConnections.Items.Add(handlerSocket.RemoteEndPoint.ToString() + " connected.");
lock (this)
{
nSockets.Add(handlerSocket);
}
ThreadStart thdstHandler = new
ThreadStart(handlerThread);
Thread thdHandler = new Thread(thdstHandler);
thdHandler.Start();
}
}
}
public void handlerThread()
{
Socket handlerSocket = (Socket)nSockets[nSockets.Count - 1];
NetworkStream networkStream = new NetworkStream(handlerSocket);
int thisRead = 0;
int blockSize = 1024;
Byte[] dataByte = new Byte[blockSize];
lock (this)
{
// Only one process can access
// the same file at any given time
Stream fileStream = File.OpenWrite("c:\\my documents\\SubmittedFile.txt");
while (true)
{
thisRead = networkStream.Read(dataByte, 0, blockSize);
fileStream.Write(dataByte, 0, thisRead);
if (thisRead == 0) break;
}
fileStream.Close();
}
lbConnections.Items.Add("File Written");
handlerSocket = null;
}
}
}
TCP Client
using Microsoft.Win32;
using System.IO;
using System.Net.Sockets;
using System.Windows;
using System.Threading;
using System.Net;
using System.Text;
namespace SimpleTCPClient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void browseButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
fileTextbox.Text = (openFileDialog.FileName);
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
Stream fileStream = File.OpenRead(fileTextbox.Text);
// Alocate memory space for the file
byte[] fileBuffer = new byte[fileStream.Length];
fileStream.Read(fileBuffer, 0, (int)fileStream.Length);
// Open a TCP/IP Connection and send the data
TcpClient clientSocket = new TcpClient(ipTextbox.Text, 8080);
NetworkStream networkStream = clientSocket.GetStream();
networkStream.Write(fileBuffer, 0, fileBuffer.GetLength(0));
networkStream.Close();
}
}
}
if lbConnections is a client control, you cannot modify it from a background thread - you must invoke a method that will do the updating on the GUI thread. There's effectively no thread safety around GUI controls, and all disabling Control.CheckForIllegalCrossThreadCalls does is make it less obvious when you shoot yourself in the foot.
In general, you have other problems. For example, your handler thread always tries to grab the last connection - there's a very real possibility that two connections could happen (and both be added to nSockets) before either of the handler threads try to grab the sockets - meaning one socket never gets grabbed and one gets grabbed twice. Plus, it looks like you never actually dispose any of the socket objects, or even allow them to be GCd, since I don't see any code that removes from nSockets.
You really have two good options - either pass the handler thread the data it needs, or use something like a queue and have the handler thread dequeue one socket and operate on it (of course all queue access must be synchronized). Either way you should be disposing of the sockets and not keeping them around forever.
Related
I am trying to send a message to Unity through UDP. The machine that sends the message has IP as 192.16.14.1 and port as 3034. How do I enter these two inside of Unity application? I have found a code to listen for UDP messages but I cannot set the IP address here. Also the Unity application should be running at all times even if the message from another machine is sent or not.
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public class UDP_Listen : MonoBehaviour
{
UdpClient clientData;
int portData = 3034;
public int receiveBufferSize = 120000;
public bool showDebug = false;
IPEndPoint ipEndPointData;
private object obj = null;
private System.AsyncCallback AC;
byte[] receivedBytes;
void Start()
{
InitializeUDPListener();
}
public void InitializeUDPListener()
{
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
clientData = new UdpClient();
clientData.Client.ReceiveBufferSize = receiveBufferSize;
clientData.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
clientData.ExclusiveAddressUse = false;
clientData.EnableBroadcast = true;
clientData.Client.Bind(ipEndPointData);
clientData.DontFragment = true;
if (showDebug) Debug.Log("BufSize: " + clientData.Client.ReceiveBufferSize);
AC = new System.AsyncCallback(ReceivedUDPPacket);
clientData.BeginReceive(AC, obj);
Debug.Log("UDP - Start Receiving..");
}
void ReceivedUDPPacket(System.IAsyncResult result)
{
//stopwatch.Start();
receivedBytes = clientData.EndReceive(result, ref ipEndPointData);
ParsePacket();
clientData.BeginReceive(AC, obj);
//stopwatch.Stop();
//Debug.Log(stopwatch.ElapsedTicks);
//stopwatch.Reset();
} // ReceiveCallBack
void ParsePacket()
{
// work with receivedBytes
Debug.Log("receivedBytes len = " + receivedBytes.Length);
}
void OnDestroy()
{
if (clientData != null)
{
clientData.Close();
}
}
}
If the Unity application is to be receiving the messages constantly, it needs to be something like:
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("192.16.14.1"), 3034);
while (true)
{
byte[] bytes = listener.Receive(ref groupEP);
}
This should read only calls from the specific IP, not sure which port you want the UDPClient to read out from (specified in the UDPClient constructor) but you can set this to whatever you need it to be.
So there are two different things:
You want to define the receiving local port you Bind your socket to
You want to define the expected sending remote ip + port you want to Receive from
Currently you are using the very same one
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
for both! (Fun fact: As a side effect by using always the same field you basically allow any sender but are then bond to that specific sender from this moment on)
Actually a lot of things you configure there are the default values anyway so here is more or less what I would do
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class UDP_Listen : MonoBehaviour
{
public ushort localReceiverPort = 3034;
public string senderIP = "192.168.111.1";
public ushort remoteSenderPort = 3034;
public bool showDebug = false;
// Thread-safe Queue to handle enqueued actions in the Unity main thread
private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
private Thread udpListenerThread;
private void Start()
{
// do your things completely asynchronous in a background thread
udpListenerThread = new Thread(UDPListenerThread);
udpListenerThread.Start();
}
private void Update()
{
// in the Unity main thread work off the actions
while (mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
private void UDPListenerThread()
{
UdpClient udpClient = null;
try
{
// local end point listens on any local IP
var localEndpoint = new IPEndPoint(IPAddress.Any, localReceiverPort);
udpClient = new UdpClient(localEndpoint);
if (showDebug)
{
Debug.Log("BufSize: " + clientData.Client.ReceiveBufferSize);
}
Debug.Log("UDP - Start Receiving..");
// endless loop -> ok since in a thread and containing blocking call(s)
while (true)
{
// remote sender endpoint -> listens only to specific IP
var expectedSenderEndpoint = new IPEndPoint(IPAddress.Parse(senderIP), remoteSenderPort);
// blocking call - but doesn't matter since this is a thread
var receivedBytes = udpClient.Receive(ref expectedSenderEndpoint);
// parse the bytes here
// do any expensive work while still on a background thread
mainThreadActions.Enqueue(() =>
{
// Put anything in here that is required to happen in the Unity main thread
// so basically anything using GameObject, Transform, etc
});
}
}
// thrown for "Abort"
catch (ThreadAbortException)
{
Debug.Log("UDP Listener terminated");
}
// Catch but Log any other exception
catch (Exception e)
{
Debug.LogException(e);
}
// This is run even if an exception happend
finally
{
// either way dispose the UDP client
udpClient?.Dispose();
}
}
private void OnDestroy()
{
udpListenerThread?.Abort();
}
}
I'm sure the same can be done also using the BeginReceive/EndReceive or task based alternatives but since it is going to run endless anyway I personally find a thread often easier to read and maintain.
I think you got it backwards. This code you shared is for, like you said, listen UDP protocol on desired port. This piece of code needs to be inside your "server". By server try to understand that as the receiving side.
on your shared method InitializeUDPListener(); we have this piece:
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
this means you are initializing your udp socket to listen for ANY ip adresses at the given port. That said, you have your server ready to go, what you need to do is setup the client side, the one who sends the message.
here some example:
public string serverIp = "127.0.0.1"; // your server ip, this one is sending to local host
public int serverPort = 28500; // your server port
public void ClientSendMessage()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress broadcast = IPAddress.Parse(serverIp);
byte[] sendbuf = Encoding.ASCII.GetBytes("THIS IS A MESSAGE FROM CLIENT!");
IPEndPoint ep = new IPEndPoint(broadcast, serverPort);
s.SendTo(sendbuf, ep);
}
I encourage you to read about UDP/TCP protocols before using them. MS has documentation with details.
here some links:
TCP
UDP
Sockets
Whenever a client Disconnects, the server crashes. Here's the code for the server
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;
namespace C_Sharp_Testting
{
class Server
{
private static TcpListener tcpListener;
private static List<TcpClient> tcpClientsList = new List<TcpClient>();
static void Main(string[] args)
{
tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
Console.WriteLine("Server started");
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
tcpClientsList.Add(tcpClient);
Thread thread = new Thread(ClientListener);
thread.Start(tcpClient);
}
}
public static void ClientListener(object obj)
{
TcpClient tcpClient = (TcpClient)obj;
StreamReader reader = new StreamReader(tcpClient.GetStream());
Console.WriteLine("Client connected");
while (true)
{
string message = reader.ReadLine();
BroadCast(message, tcpClient);
Console.WriteLine(">>> "+message);
}
}
public static void BroadCast(string msg, TcpClient excludeClient)
{
foreach (TcpClient client in tcpClientsList)
{
if (client != excludeClient)
{
StreamWriter sWriter = new StreamWriter(client.GetStream());
sWriter.WriteLine(">>> "+msg);
sWriter.Flush();
}
}
}
}
}
I've already tried closing the reader and tcpClient, but none of them worked.
The thing that initially hits me is that you have not added any error handling to this code.
When you try to read from a disconnected socket, you will encounter an exception, which will crash your application.
You add a try and catch statement to the ClientListener method, to allow each Socket the ability to manage and handle its own errors.
This means you will be able to detect disconnects and handle them gracefully.
Consider implementing events.
Create an event called OnDisconnect, and then add your own handler to the event to remove the disconnected client from the list of clients.
/// <summary>
/// Event is triggered when the peer is disconnecting
/// </summary>
public event DisconnectHandler OnDisconnect;
public delegate void DisconnectHandler(Peer p);
This is an extension class
static class SocketExtensions
{
/// <summary>
/// Extension method to tell if the Socket REALLY is closed
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public static bool IsConnected(this Socket socket)
{
try
{
return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (SocketException) { return false; }
}
}
Pseudocode below:
if (PeerStream.CanRead)
{
//networkStream.Read(byteLen, 0, 8)
byte[] byteLen = new byte[8];
if (_client.Client.IsConnected() == false)
{
//Fire Disconnect event
if (OnDisconnect != null)
{
disconnected = true;
OnDisconnect(this);
return null;
}
}
while (len == 0)
{
PeerStream.Read(byteLen, 0, 8);
len = BitConverter.ToInt32(byteLen, 0);
}
data = new byte[len];
PeerStream.Read(data, receivedDataLength, len);
return data;
There are many things wrong with your code. At a guess, I would say the primary reason for the crash is that you are adding TcpClients to a list but never removing them. That means regardless of client disconnects, your code still tries to access every TcpClient that ever connected.
On top of that, the code is inherently thread-unsafe. You are adding items to a list in one thread while using a foreach loop to iterate over the list at the same time in a different thread - this will almost certainly result in an exception being thrown.
Lastly, there's no try-catch blocks. If your code is crashing, a simple improvement is to wrap the problem areas with try-catch blocks and handle/log/examine the exceptions when they occur.
You don't have any code to detect that any of the clients have closed its connection. You keep doing reader.Readline. But that won't work. You should use a networkstream instead and check for receiving 0 bytes which indicates the client has closed its end of the connection. It is either this or using exceptionhandling. But receiving 0 bytes is not really an error condition. IMHO you should not use exceptions to capture normal logic flow but real errors. This does not mean you don't have to wrap everything in a try catch block though. You still have to do that too.
from https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read(v=vs.110).aspx:
This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.'
When a client disconnects and detected properly you can do all that is needed to make it work properly, like removing it from the list, letting the thread terminate and so on.
Note also that the tcpClientsList needs some kind of semaphore protection otherwise multiple threads are accessing this same list which can lead to strange behavior that happens only now and then.
I put a lot of information in this issue because I have no idea what will be relavent
Issue:
I am having an issue with a program I am working on where when running it, it will freeze my whole computer and return no error (I am completely incapable of doing anything CTRL+ALT+DEL doesn't even work). This program accepts a connection from a android client and atm the android client is not configured correctly so the connection is being rejected.
Question:
How can I stop my program from freezing my entire machine?
Conjecture:
I have a few theories as to what is going on but no idea how to fix them. I have read that this may have something to do with me running a single threaded process inside my async worker but I am not sure that the socket is a single threaded process. Also I am not entirely sure how I am supposed to deal with exceptions in a backgroundworker so I just let it fall back to the RunWorkerCompletedEventArgs then retrieve the error message from there.
What I have tried:
- I tried putting try catches every where then removing try catches nothing seems to be able to capture this error
- I checked my systems event log and nothing is showing up except my restarts after my computer freezes
- I have attempted to isolate the issue but it can literally happen at any point from the program starting till when I attempt to connect
Setup:
I am running the program out of visual studio 2012 professional on a windows 8 pro machine. The computer I am on has a i7-3770K 3.50GHz and 32GB of ram. The application that is attempting to make a connection to mine is a Android application and the credentials are incorrect when it is attempting to connect. Visual Studio is running off my main hard drive and building the project on another drive.
Closing:
With all that said would some one please be willing to help me? If you need any more information I will be happy to provide it, please ask.
Main Method:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace Server
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class SourceServer : Window
{
private BackgroundWorker worker = new BackgroundWorker();
public SourceServer()
{
InitializeComponent();
StartListeningForConnections();
}
private void StartListeningForConnections()
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.WorkerReportsProgress = true;
if (worker.IsBusy != true)
{
worker.RunWorkerAsync();
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
worker.ReportProgress(0, "Source server version 0.0.0.1ib started");
LoginServer oLoginServer = new LoginServer();
oLoginServer.StartListening(worker);
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
lvOutput.Items.Add(e.UserState.ToString());
}
catch (Exception exception)
{
lvOutput.Items.Add(exception.StackTrace);
}
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
System.IO.File.WriteAllText(Environment.CurrentDirectory + #"\log.txt", e.Error.StackTrace + " /n " + e.Error.Message);
}
else
{
MessageBox.Show("Error was null");
}
worker.Dispose();
}
}
}
SSL Socket Connection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using MySql.Data.MySqlClient;
using System.IO;
namespace Server
{
public class LoginServer
{
// Incoming data from the client.
public static string data = null;
public static X509Certificate serverCertificate = null;
public delegate void UpdateListView(ListView oOutput);
public void StartListening(BackgroundWorker worker)
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[1];
serverCertificate = X509Certificate.CreateFromCertFile(#"server.crt");
TcpListener oServer = new TcpListener(ipAddress, 12345);
// Bind the socket to the local endpoint and
// listen for incoming connections.
// Start listening for connections.
while (true)
{
Thread.Sleep(100);
worker.ReportProgress(0, "Waiting for connection....");
// Program is suspended while waiting for an incoming connection.
//Socket handler = listener.Accept();
oServer.Start();
TcpClient oClient = oServer.AcceptTcpClient();
Stream oStream = oClient.GetStream();
SslStream oSSLStream = new SslStream(oStream);
data = null;
// An incoming connection needs to be processed.
string sUsername = "place holder";
string sPassword = "place holder";
while (true)
{
bytes = new byte[1024];
int bytesRec = oSSLStream.Read(bytes, 0, bytes.Length);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
string[] sCredentials = data.Split("|".ToCharArray()[0]);
sUsername = sCredentials[0];
sPassword = sCredentials[1];
if (data.IndexOf("<EOF>") > -1)
{
break;
}
}
// Show the data on the console.
worker.ReportProgress(0, "Connection Recieved : ");
worker.ReportProgress(0, "Username: " + sUsername);
worker.ReportProgress(0, "Password: " + sPassword);
worker.ReportProgress(0, "");
// Echo the data back to the client.
byte[] msg;
if (sUsername.Equals("test") && sPassword.Equals("test"))
{
msg = Encoding.ASCII.GetBytes("approved<EOF>\n");
worker.ReportProgress(0, "approved");
oSSLStream.Write(msg, 0, msg.Length);
}
else
{
msg = Encoding.ASCII.GetBytes("rejected<EOF>\n");
worker.ReportProgress(0, "rejected");
oSSLStream.Write(msg, 0, msg.Length);
}
}
}
public void VerifyUser()
{
}
}
}
While I don't see any reason for this to lock up your entire computer, I do see a couple of reasons for the application to potentially hang...
Your while loop inside of your SSL server will never break unless your client writes '<EOF>' to the stream; which you would have to force it to do. I would likely do something similar to this:
while(( bytesRec = oSSLStream.Read(bytes,0,bytes.Length)) > 0 )
{
// Compare input & break
}
-- The while loop you have now ( without a thread sleep ) will consume all of your systems resources waiting for ... something that may never occur.
In a related issue - I note that your 'DoWork' method launches the listener - but does not start a new thread for this listener. This means that the listener is running inside of your interface thread - which will cause the interface ( and potentially more... ) to hang until the process is completed - which as stated, may never happen.
... Ahem... This last paragraph may be incorrect - you are running an async worker, so I may be incorrect in my second assessment.
Cheers, hope this is helpful.
I've had some hanging problems on Windows 8 that I never saw on Windows 7 (with VS2012). As you experienced it worked fine the first time but only locked up Visual Studio (and not my whole machine) and I had to force quit.
The Visual Studio 2012 Update 4 (which focuses on bug fixes and compatibility) seemed to fix it, although I didn't scientifically test this.
Note: As of 9/1/13 this is only the RC2 version so please check for newer versions, and edit this answer when RTM happens.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 9 years ago.
Improve this question
This is not actually a question. I'm trying to organize what I've learned about this subject this year. Since I am a beginner in C#, I had lots of difficulties doing this. But thanks to Stack Overflow and a lecture about packets in class, I was able to get enough information to write a program using multiple types of packets, and connections. This script is for beginners who do not know what to do about sockets.
The concept of sending a packet seems to be sending a whole class over the connection. Not writing the data directly to the stream. So, I have to make a DLL(Class Library) file which defines the packet class, and the derived classes of the packet class.
I'll write a simple code for the Packet.dll, which will be having one type of packet, the Login class.
*To make a DLL file, simply make a C# Class Library File on the VS. To compile it, press F7.
"Project Packet, Packet.cs"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Packet
{
public enum PacketType
{
initType = 0, //it's nothing on this code.
login
}
[Serializable]
public class Packet
{
public int Length;
public int Type;
public Packet() {
this.Length = 0;
this.Type = 0;
}
public static byte[] Serialize(Object o) {
MemoryStream ms = new MemoryStream(1024 * 4); //packet size will be maximum of 4KB.
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, o);
return ms.ToArray();
}
public static Object Desirialize(byte[] bt) {
MemoryStream ms = new MemoryStream(1024 * 4);//packet size will be maximum of 4KB.
foreach( byte b in bt){
ms.WriteByte(b);
}
ms.Position = 0;
BinaryFormatter bf = new BinaryFormatter();
object obj = bf.Deserialize(ms);
ms.Close();
return obj;
}
}
}//end of Packet.cs
add a new class for this Project Packet, "Login.cs".
"Project Packet, Login.cs"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Packet
{
[Serializable] //to serialize this class, this statement is essential.
public class Login :Packet //derived class of Packet.cs. Must be public.
{
public string id_str; //id
public string pw_str; //pw
public Login(string id, string pw) { //constructer to make the code more shorter.
this.id_str = id;
this.pw_str = pw;
}
}
}//end of Login.cs
After you are finished with this, press F7 to compile, and you will get Packet.dll on your Debug folder of the Packet project file.
This is all about the Packet class. If you want to add more classes to serialize, just add a new class, and add a enum value on the PacketType.
Next, I'll write a short example source for using the Packet class.
Although it's a simple source using only one connection, and using only one type of packet, it will be written in multiple threads.
The original of this source which I wrote has many types of packets, and is expected to get multiple connections from multiple users, so I made the class "UserSocket" to make an instance of a connected user. And also, it will have the receiving thread function(A thread function for receiving packets from the client.) in another class "MessageThread.cs".
"Project Server, Form1.cs" //a Windows Form Project, which only has a textBox named textBox1.
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.Threading;
using System.Net;
using System.Net.Sockets;
using Packet; //to add this, right click your project viewer and reference add Packet.dll.
using System.IO;
namespace Server
{
public partial class Form1 : Form
{
private TcpListener server_Listener;
public List<UserSocket> user_list = new List<UserSocket>(); //list to store user instances
private Thread server_Thread; //thread for getting connections.
public void setLog(string msg) //a function to write string on the Form1.textBox1.
{
this.BeginInvoke((MethodInvoker)(delegate()
{
textBox1.AppendText(msg + "\n");
}));
}
private void Form1_Load(object sender, EventArgs e)
{
server_Thread = new Thread(new ThreadStart(RUN)); //starts to wait for connections.
server_Thread.Start();
}
public void RUN() // Thread function to get connection from client.
{
server_Listener = new TcpListener(7778);
server_Listener.Start();
while (true)
{
this.BeginInvoke((MethodInvoker)(delegate()
{
textBox1.AppendText("Waiting for connection\n");
}));
UserSocket user = new UserSocket(); Make an instance of UserSocket
user.UserName = " ";
try
{
user.client = server_Listener.AcceptSocket();
}
catch
{
break;
}
if (user.client.Connected)
{
user.server_isClientOnline = true;
this.BeginInvoke((MethodInvoker)(delegate()
{
textBox1.AppendText("Client Online\n");
}));
user.server_netStream = new NetworkStream(user.client); //connect stream.
user_list.Add(user);
MessageThread mThread = new MessageThread(user, this, user_list); //make an instance of the MessageThread.
user.receiveP = new Thread(new ThreadStart(mThread.RPACKET)); //run the receiving thread for user.
user.receiveP.Start();
}
}
} //end of Form1.cs
"Project Server, UserSocket.cs"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using Packet;
namespace Server
{
public class UserSocket //Just a Class to make an instance of the connected user.
{
public NetworkStream server_netStream;
public bool server_isClientOnline = false;
public byte[] sendBuffer = new byte[1024 * 4];
public byte[] readBuffer = new byte[1024 * 4];
public string UserName = null; //Not in this code, but on the original, used to identify user.
public Login server_LoginClass;
public Socket client = null;
}
}//end of UserSocket.cs
"Project Server, MessageThread.cs"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Packet;
using System.IO;
namespace Server
{
public class MessageThread //A Class for threads for each users.
{
UserSocket uzr;
Form1 f;
List<UserSocket> user_list = new List<UserSocket>();
public MessageThread(UserSocket u, Form1 formget, List<UserSocket> u_l) //Constructer.
{
uzr = u;
f = formget;
this.user_list = u_l;
}
public void RPACKET() //Thread function for receiving packets.
{
f.setLog("rpacket online");
int read = 0;
while (uzr.server_isClientOnline)
{
try
{
read = 0;
read = uzr.server_netStream.Read(uzr.readBuffer, 0, 1024 * 4);
if (read == 0)
{
uzr.server_isClientOnline = false;
break;
}
}
catch
{
uzr.server_isClientOnline = false;
uzr.server_netStream = null;
}
Packet.Packet packet = (Packet.Packet)Packet.Packet.Desirialize(uzr.readBuffer);
//Deserialize the packet to a Packet.cs Type. It's because the packet.Type is in the super class.
switch ((int)packet.Type)
{
case (int)PacketType.login: //If the PacketType is "login"
{
uzr.server_LoginClass = (Login)Packet.Packet.Desirialize(uzr.readBuffer);
f.setLog("ID : " + uzr.server_LoginClass.id_str + " PW : " + uzr.server_LoginClass.pw_str);
uzr.UserName=uzr.server_LoginClass.id_str;
}
}
}
}
}
}
This will be all for the Server part. It starts a listening thread on the form_load to get connections, and if it's connected to a client, it will make an instance of UserSocket, and the connection will be made by the UserSocket.client(Socket client). And it will bind the socket with the NetworkStream of the UserSocket, and start a listening thread.
The listening thread will deserialize packets received by the client, and assign the received class to a member class of UserSocket.
Next, it will be the sending part of this script. The client part.(On the original source, it can send, and also receive packets from the server, but on this script, I'll just make it send a packet. To receive a packet, just make a thread, and a thread function simular to the server on the main page.
"Project Client, Form1.cs"
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.Threading;
using System.Net;
using System.Net.Sockets;
using Packet;
using System.IO;
namespace Client
{
public partial class Form1 : Form //A Simple Windows Form Project with Two TextBoxes, and a Button
{
private string myid;
private NetworkStream client_Netstream;
private byte[] sendBuffer = new byte[1024 * 4];
private byte[] receiveBuffer = new byte[1024 * 4];
private TcpClient client_tcpClient;
private bool client_isOnline = false;
public Login login;
private void Form1_Load(object sender, EventArgs e)
{
//On Form1 Load, It will connect to the server directly. So, the Server must be active before executing the client.
this.client_tcpClient = new TcpClient();
try
{
this.client_tcpClient.Connect("localhost", 7778);
}
catch
{
MessageBox.Show("Connection Failure\n");
return;
}
this.client_isOnline = true;
this.client_Netstream = this.client_tcpClient.GetStream();
}
private void button1_Click(object sender, EventArgs e)
{
if (!this.client_isOnline)
return;
login = new Login();
login.Type = (int)PacketType.login; //Very essential. must be defined for the server to identify the packet.
login.id_str = this.textBox1.Text;
login.pw_str = this.textBox2.Text;
Packet.Packet.Serialize(login).CopyTo(this.sendBuffer, 0);
this.client_Netstream.Write(this.sendBuffer, 0, this.sendBuffer.Length);
this.client_Netstream.Flush();
for (int i = 0; i < 1024 * 4; i++)
this.sendBuffer[i] = 0;
}
}
}//End of Form1.cs
As I said above, this client won't have a receiving thread. So this is all for the client.
It connects to the server on form load, and if you press the button1, the value of textbox1, and textbox2 will be sent to the server as a serialized packet, PacketType of 'login'. On this example the client just sends two variables, but it can send bigger classes such as classes with Lists.
This is all I can explain about socket programming on C# using Packets. I tried to make it simple, but I couldn't make it shorter. For begginers like me, if you have a question, please leave a comment, and for more skilled experts, if this code needs modification for more efficient coding, please tell me by answering this script.
This is a very long question and I am unclear what the key point is, but:
The concept of sending a packet seems to be sending a whole class over the connection. Not writing the data directly to the stream. So, I have to make a DLL(Class Library) file which defines the packet class, and the derived classes of the packet class.
No. In TCP packets are largely an implementation detail. The socket exposes a stream of data, without any further definitions of logical or physical splits. You can invent your own partitioning / framing any way you like within that, and it doesn't need to map to any "whole class" - it can entirely be "the data" if you like. The point is, however, that "writing data to a stream" is pretty convenient to handle via serializers, and serializers work nicely with a "whole class". However, in most of my socket work the data is more subtle than that and is processed manually and explicitly.
For more general socket guidance, consider http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html
Ok, I studied a bit on asynchronous TCP network connection. I tried making one but failed. What I want to do is make sure the server or client is always ready to receive a chat and is able to send a chat any time. I do not want them to be on alternate mode.
e.g. Server Send while client waits to receive thus client can't send at that time.
I do not want that!
Did this on Windows Application. Once I connected, the system resource just shot to 100% =/
Server 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;
using System.Net.Sockets;
namespace AsyncServerChat
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Socket g_server_conn;
private byte[] g_bmsg;
private bool check = false;
private void Form1_Load(object sender, EventArgs e)
{
IPEndPoint local_ep = new IPEndPoint(IPAddress.Any, 9050);
Socket winsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
winsock.Bind(local_ep);
winsock.Listen(5);
winsock.BeginAccept(new AsyncCallback(Accept), winsock);
}
private void Accept(IAsyncResult iar)
{
Socket server_conn =(Socket) iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
//label1.Text = "Connected. . .";
while (g_server_conn.Connected && check == false)
{
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), g_server_conn);
}
}
private void Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void Recieve(IAsyncResult iar)
{
Socket server_conn =(Socket) iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
check = false;
}
}
private void sendButton_Click(object sender, EventArgs e)
{
string strmsg = textBox1.Text;
byte[] bmsg= Encoding.ASCII.GetBytes(strmsg);
g_server_conn.BeginSend(bmsg, 0, bmsg.Length, SocketFlags.None, new AsyncCallback(Send), g_server_conn);
}
}
}
Client
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace AsyncClientChat
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Socket g_client_conn;
byte[] g_bmsg;
private bool check = false;
private void Form1_Load(object sender, EventArgs e)
{
}
private void connectButton_Click(object sender, EventArgs e)
{
IPEndPoint remote_ep = new IPEndPoint(IPAddress.Parse(textBox1.Text), 9050);
g_client_conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
g_client_conn.BeginConnect(remote_ep, new AsyncCallback(Connect), g_client_conn);
}
private void Connect(IAsyncResult iar)
{
Socket client_conn =(Socket) iar.AsyncState;
client_conn.EndConnect(iar);
while (g_client_conn.Connected)
{
g_bmsg = new byte[1024];
check = true;
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), g_client_conn);
}
}
private void Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void Recieve(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
check = false;
}
}
}
}
Well, the problem is the while loop in client method Connect.
Remove it because it loops infinitely raising CPU usage to 100% and it's useless.
BTW, you have another problems in your code:
CrossThread operation exception
For example in your Client.Recieve method you do:
label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
Actually, you're trying to set the label text from another thread (the one listening for received msgs) and this is not allowed;
Do something like this:
Create a Setter method for the label text:
private void SetLabelText(string txt)
{
if (label1.InvokeRequired)
label1.Invoke(new MethodInvoker(delegate { SetLabelText1(txt); }));
else
label1.Text = txt;
}
then use the setter instead of directly call label1.Text = ...:
SetLabelText(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
EDIT to answer to OP comment:
For a good and extensive explanation of what is a thread, look at its wikipedia page.
Anyway, in simple words, a running process contains one or more threads and these are part of code that can be executed concurrently.
Starting from your TCP example, using Socket.Receive instead of Socket.BeginReceive you would have blocked the execution on Socket.Receive() call (I mean the lines of code after the one containing Receive method wouldn't be reached) until something is received.
This because Socket.Receive method runs on the same thread of the following code, and on each thread, the code is executed sequentially (i.e. line by line).
Conversely, using Socket.BeginReceive, behind the scene a new thread is created. This thread likely calls and stops on Socket.Receive method, and once received something it calls the method passed as parameter.
This makes Socket.BeginReceive asynchronous, while Socket.Receive is synchronous, and this is why I knew ther was another thread (when you hear asynchronous word, is extremely probable that you are dealing with multi-threading)
Thus, when you change label.Text you are actually setting it from another thread: the one created by Socket.BeginReceive.
I took a quick look at this code and I would start with the following suggestion.
Remove the looping from your Accept callback, just initiate the BeginReceive and let it be. Then in your Receive method, you can just initiate the next BeginReceive. This would apply for both the client and the server code, except of course for the client code you will remove the loop from your Connect callback method.
Then you should also watch out updating the UI controls from the callback methods, since the callback runs on the non-UI thread which can cause a host of problems. You should look at using Control.Invoke or Control.BeginInvoke to marshal a request back to the UI thread which can then update the controls.