C# basic Socket programming using Packets [closed] - c#

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

Related

How do I assign this value to a global variable in c#?

I need to assign a value that's being received from a socket (TCP/IP) to a variable, so I can use it in a label in a form.
I'm asking here because I've been searching and trying for hours and can't find anything.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
namespace ConsoleApp1
{
class Exemys
{
static byte[] Buffer { get; set; }
static Socket sck;
[STAThread]
public static void Conectar(/*string[] args*/)
{
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Parse("192.168.34.230"), 5202);
try
{
sck.Connect(localEndpoint);
Console.WriteLine("Exemys connected!\r\n");
}
catch
{
Console.Write("Unable to connect to Exemys\r\n");
Conectar(/*args*/);
}
while (true)
{
Buffer = new byte[sck.SendBufferSize];
int bytesRead = sck.Receive(Buffer);
byte[] formatted = new byte[bytesRead];
for (int i = 0; i < bytesRead; i++)
{
formatted[i] = Buffer[i];
}
string mensaje = Encoding.ASCII.GetString(formatted);
Console.Write(mensaje + "\r\n");
}
}
}
}
This code is written in a class, and the Form is in other place.
The value that I need to assign is mensaje, so I can see it in a Text Box in the Form.
First of all global variables smells like a bad design. Anyway, it seems like you are giving an example of a ConsoleApplication but you have maybe a Windows Form Application? Obviously you can not run a Form from a Console Application, so just convert everything to a Windows Forms Application to start with.
Anyway, after you have a Form working, you will probably could change your code so that Conectar(), that we in fact we can give a better name like ConnectAndGetMessage(), to actually return the message. Then you call that method from your Form, maybe on the Load handler.
Briefly, you could do something like this:
class Exemys {
public static string ConnectAndGetMessage() {
// Here your code! ;)
return mensaje;
}
}
class FormWithTextbox {
private void FormWithLabel_Load(object sender, System.EventArgs e)
{
Textbox1.Text = Exemys.ConnectAndGetMessage();
}
}
Please note that this is not the best solution because you should inject the Exemys as a dependency instead, anyway, that will be for a later improvement.
Another thing to point out about your code, but you will probably get to know quickly is that you are not exiting the while loop, so you will probably end up in an infinite loop.

Why is my object being used by another thread? [duplicate]

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.

Cannot Instantiate byte[] array in C# coding scripts inside Unity3D; How do I fix that?

So I am trying to send/receive messages between my unity project and another window that acts as the server. I cannot use stream.Read() with the byte array I have now. It says it is a NullReferenceException and is not instantiated though I think it is. It may be something super simple, but it is really bugging me lol. Here's the code; the line that gives the exception is the stream.Read(....):
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.IO;
public class Client_Script : MonoBehaviour {
byte[] SendBuffer = null;
TcpClient client = null;
NetworkStream stream = null;
int PortNumber = 3003;
string IP = "192.168.1.100";
// Use this for initialization
void Start () {
try
{
SendBuffer = new byte[1024];
client = new TcpClient();
client.Connect(IP, PortNumber);
}
catch (Exception e)
{
//"Couldn't connect to destination"
}
stream = client.GetStream();
}
// Update is called once per frame
void Update () {
stream.Read (SendBuffer, 0, 1024);
string message = BitConverter.ToString (SendBuffer);
ASCIIEncoding encoder = new ASCIIEncoding ();
SendBuffer = encoder.GetBytes (message);
stream.Write (SendBuffer, 0, SendBuffer.Length);
}
}
===============================================================
It really isn't much code; I am just frustrated at how it says it is not instantiated even though it is. It may have no valid data in it, but I created it anyways. It works in Visual Studio projects, just not when I use it inside of Unity3D.
You need to make doubly sure that Update() isn't being called before a call to Start() - seen this dozens of times in code where people hook up event handlers first, then they initialize.
In the present state, if there's an exception inside of Start(), you're not handling it at all by the looks of it, so 'client' may be returning a null value and this is what actually be the cause of the exception.

WPF C# application will freeze my whole computer ever 2-3 times I run it

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.

Getting multi-threading to synchronize between web page and Business layer

I'm having a problem getting this chunk of code to work (sorry for the length). The code is sterilized to just show the relevant portions in diagnosing the problem.
It works fine when run from a console app.
But when Utlities.SendBulkEmail is called from an ASP.NET app, the BulkEmailCompleted routine never fires, and it is this routine that increments the m_CompletedWorkers counter.
How do I refactor the SendBulkEmail routine to use AsyncOperationManager instead of BackgroundWorker, so I can guarantee the thread that the results are returned on.
The SendBulkEmail routine itself is not multi-threaded. The multi-threading happens inside its foreach loop.
I think the basis of the original code was gotten from this website:
http://www.dotnetfunda.com/articles/article613-background-processes-in-asp-net-web-applications.aspx
The Utilities project is shared among various solutions, and is pretty much stand alone.
I'm hoping that I'm making this clear.
Any help will be appreciated.
The code follows:
IN THE WEBSITE PROJECT (Control.ascx.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Configuration;
using <company>.BusinessObjects.Emails;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Interfaces;
using <company>.Utilities;
...
protected void sendButton_OnClick(object sender, EventArgs e)
{
...
if (HasBenefits)
{
ReportingEmails emailer = new ReportingEmails();
...
//Prevent send if nothing selected
if (... > 0)
{
List<EmailOutcome> results = emailer.GenerateNotificationEmailsForEmployer(<some int>, <some list>);
...
}
}
...
}
IN THE BUSINESS OBJECT PROJECT
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Mail;
using System.Resources;
using System.Text;
using System.IO;
using <company>.BusinessObjects.Emails.Helpers;
using <company>.DataAccessors;
using <company>.DataObjects;
using <company>.Enumerations;
using <company>.Utilities;
namespace <company>.BusinessObjects.Emails
{
public class ReportingEmails
{
...
public List<EmailOutcome> GenerateNotificationEmailsForEmployer(int employerID, List<int> benefits = null)
{
...
SendNotificationEmails(List<ReportData>, ref emailSuccessByBenefit, true, benefitsToExclude);
return emailSuccessByBenefit;
}
private void SendNotificationEmails(List<ReporterCommsData> reportData, ref List<EmailOutcome> emailSuccessByBenefit, bool isFleet, List<int> benefitsToExclude)
{
Dictionary<int, MailMessage> bulkEmails = new Dictionary<int, MailMessage>();
//build up the set of emails to send
foreach (ReporterCommsData report in reportData)
{
...
if (String.IsNullOrEmpty(report.Email))
{
...
}
else
{
try
{
MailMessage email = null;
...
email = ConstructEmail(<param>, out <param>, <param>);
...
bulkEmails.Add(report.BenefitID, email); //add each email to the bulk email dictionary
...
}
catch (Exception ex)
{
...
}
}
} //end foreach
//do the bulk email send and get the outcomes
try
{
...
emailSuccessByBenefit.AddRange(Utilities.Mail.SendBulkEmail(bulkEmails, credentials));
}
catch (Exception ex)
{
...
}
}
...
}
...
}
IN THE UTILITIES PROJECT
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Threading;
namespace <company>.Utilities
{
...
public class Mail
{
private static List<EmailOutcome> m_MultithreadedEmailSendResults = new List<EmailOutcome>();
private static int m_CompletedWorkers = 0;
...
/// <summary>
/// Sends a large number of emails asynchronously and then reports success of the individual items collectively
/// </summary>
/// <param name="emails">A dictionary of completed MailMessage objects to send out, keyed on an ID</param>
/// <param name="credentials">Network credentials which may be required to send the email</param>
/// <returns>List of EmailOutcome objects signifying the success or failure of sending each individual email</returns>
public static List<EmailOutcome> SendBulkEmail(Dictionary<int, MailMessage> emails, System.Net.NetworkCredential credentials = null)
{
const int NUMBER_OF_THREADS_PER_PROCESSOR = 1;
m_CompletedWorkers = 0;
List<EmailOutcome> results = new List<EmailOutcome>();
List<Dictionary<int, MailMessage>> splitEmailList = new List<Dictionary<int, MailMessage>>();
...
List<BackgroundWorker> workerThreads = new List<BackgroundWorker>();
foreach (Dictionary<int, MailMessage> splitEmails in splitEmailList)
{
// Initialise the parameter array
Object[] parameterArray = new Object[2];
parameterArray[0] = splitEmails;
parameterArray[1] = credentials;
// Runs on function startup
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(BulkEmailWorker);
worker.WorkerReportsProgress = false;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BulkEmailCompleted);
//Add worker to collection
workerThreads.Add(worker);
//Calling the BulkEmailWorker asynchronously
worker.RunWorkerAsync(parameterArray);
}
//Hold until all background workers complete
while (workerThreads.Count > m_CompletedWorkers)
{
Thread.Sleep(500); //Wait a half second
}
//Dispose of BackgroundWorkers
foreach (BackgroundWorker worker in workerThreads)
{
worker.Dispose();
}
//Get results
results.AddRange(m_MultithreadedEmailSendResults);
//Clear the static variable
m_MultithreadedEmailSendResults.Clear();
m_MultithreadedEmailSendResults = new List<EmailOutcome>();
return results;
}
...
/// <summary>
/// Event handler for the RunWorkerCompleted event. Adds the EmailOutcome results to the static
/// </summary>
private static void BulkEmailCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//Add the EmailOutcome objects to the static list of results if completed
if (worker != null)
{
Thread.Sleep(200);
worker.RunWorkerAsync();
}
else
{
m_MultithreadedEmailSendResults.AddRange(e.Result as List<EmailOutcome>);
m_CompletedWorkers++;
}
}
...
}
...
}
The way asp.net works is that it creates a Page object for each request. The same goes for controls. Your notifications of the emails being sent can only reach this object.
The threads you create will run take their time to execute, and your response will not wait for the email sending to complete. This means that you can't send the status using the same request.
However if you make another request, ajax or otherwise from the page, to get the updated status, a new Page and corresponding control objects will be created. You will have to get the status from you static objects and use that to show the status to a user.
You may find the UpdatePanel control handy to implement ajax.

Categories