Proper way to shut down socket server in WinForms application? - c#

I'm working on a basic socket server in C# that needs to run as part of a Windows Forms application. I started with the asynchronous socket server code from MSDN. Like many socket server code samples, this one is a console mode application. Here's a sample of the StartListening() method that gets called from Main():
public static void StartListening()
{
// data buffer for incoming data
byte[] bytes = new byte[1024];
IPAddress ipAddress = IPAddress.Parse(serverIpAddress);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNumber);
// create TCP/IP socket
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// bind socket to the local endpoint and listen for incoming connections
try
{
serverSocket.Bind(localEndPoint);
serverSocket.Listen(100);
while (true)
{
// set the allDone ManualResetEvent to nonsignaled state
allDone.Reset();
// start an asynchronous socket to listen for connections
Console.WriteLine("Waiting for a connection...");
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);
// wait until a connection is made before continuing
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
As you can see, there's a while(true) loop in there which uses a ManualResetEvent to wait until a client connection is established before entering the next iteration of the while loop. I'm not sure why they included the Console.WriteLine(\n"Press ENTER to continue...") statement since there's no way to break out of the while(true) loop.
Anyway, in thinking about converting this into a WinForms application, I would obviously need to run the server on another thread but when the user closes my application, I want to make sure that the socket server gets shutdown "properly". My best guess at what that means is closing any open client connections, breaking out of the listening loop and then shutting down the server socket (not necessarily in that order).
I've seen some suggestions that recommend just making the server thread a background thread and letting it get destroyed when the application is shutdown but that doesn't seem very "clean". If, for example, a client is connected and sending data when the application is closed, what does that do to the client? Can anyone suggest a "best practice" with regard to shutting down a socket server properly?

Actually you don't need a thread, cause you're already listening asynchronously.
Just call serverSocket.BeginAccept again at the end of AcceptCallback.
Then the shutdown of your server reduces to serverSocket.Close() (obviously, serverSocket needs to be a class field).
Finally, in AcceptCallback you would need to catch the exception of EndAccept. That, btw, is the reason, why there's Console.Writeline and Console.Readkey in the example: it's executed when an exception occurs.

I would wrap that entire thing in a Task and use a cancellation token.
See https://msdn.microsoft.com/en-us/library/hh160373(v=vs.110).aspx for a good example on using tasks and cancellation tokens. You can make the cancellation token cancel the task when you want to shut down the socket server.
Inside the task an exception is raised. In the exception handler you call Socket.Close which will stop the BeginAccept call (EndAccept will be called but the Socket.Handle will be -1).

This block:
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
is there because the way this method is written it will exit the loop if an exception is thrown.
I would write it like this to fit your parameters:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
private Socket serverSocket;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
StartListening();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
serverSocket.Close();
}
public void StartListening() {
byte[] bytes = new byte[1024];
IPAddress ipAddress = IPAddress.Parse(serverIpAddress);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNumber);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try {
serverSocket.Bind(localEndPoint);
serverSocket.Listen(100);
while(true) {
allDone.Reset();
Console.WriteLine("Waiting for a connection...");
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);
// wait until a connection is made before continuing
allDone.WaitOne();
}
}
catch(Exception e) {
Console.WriteLine(e.ToString());
}
finally {
serverSocket.Close();
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
}
}

Related

C# Server - Socket not connecting and out of memory exception

I am trying to implement a simple TCP server and I basically copied the example on MSDN sans a couple of lines and tried to make it work. I have an external client trying to connect already.
This is my code:
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPEndPoint localEP = new IPEndPoint(ipHostInfo.AddressList[0], 4001);
Socket listener = new Socket(localEP.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEP);
listener.Listen(1000);
while (true)
{
listener.BeginAccept(new AsyncCallback(AcceptCnxCallback), listener);
}
}
catch (Exception e)
{
//Log here
}
This is my callback:
private void AcceptCnxCallback(IAsyncResult iar)
{
MensajeRecibido msj = new MensajeRecibido();
Socket server = (Socket)iar.AsyncState;
msj.workSocket = server.EndAccept(iar);
}
And this is the information of one of the incoming packages:
TCP:[SynReTransmit #1727889]Flags=......S., SrcPort=57411, DstPort=4001, PayloadLen=0, Seq=673438964, Ack=0, Win=5840 ( Negotiating scale factor 0x4 ) = 5840
Source: 10.0.19.65 Destination: 10.0.19.59
I basically have two issues:
If I use the while loop I get an OutOfMemoryException
I never do manage to connect to the client
Any tips on either of the two problems? Thank you in advance!
Your problem is, that you use asynchronous calls all the time. There is no wait mechanism or similar, so generally you are just creating new asynchronous callbacks in an infinite loop.
For a basic TCP I would recommend to use the simple approach and use the synchronous methods.
Accept() is blocking, so the program flow will stop until there is an ingoing connection.
while (true)
{
Socket s = listener.Accept();
buffer = new byte[BUFFER_SIZE];
s.Receive(buffer);
//Do something
s.Send(...);
}
Noe that this is just a basic example. If you want to keep your connection you might consider a new Thread for each accepted Socket, that continoues with receiving and sending data.
First problem
You are using an infinite loop to call an async method.
try it like this:
listener.BeginAccept(new AsyncCallback(AcceptCnxCallback), listener);
//add your code here (this part will be executed wile the listner is waiting for a connection.
while (true)
{
Thread.Sleep(100);
}
and change the Callbackmethod to:
private void AcceptCnxCallback(IAsyncResult iar)
{
MensajeRecibido msj = new MensajeRecibido();
Socket server = (Socket)iar.AsyncState;
msj.workSocket = server.EndAccept(iar);
//call again the listener after you get a message
listener.BeginAccept(new AsyncCallback(AcceptCnxCallback), listener);
}

C# SocketException when accepting connections

I have been trying to learn about sockets for the past day or so. I thought it would be a good idea to make a basic chat client and server to learn with, I have tried to make an asynchronous server so I don't need to use loads of threads etc and I have came into an issue I simply can't fix. When I start my server, it goes through all ok and waits at the point where it needs to wait for a connection. I then start up my makeshift 'client' that simply sends a string for now and my server crashes with a SocketException with the message
Additional information: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
I don't see how my socket is not connected when it has to accept the connection in the first place. I have been using this tutorial (https://msdn.microsoft.com/en-us/library/fx6588te(v=vs.110).aspx) as a guide and have looked at both my code and the tutorial and still don't understand what I am doing wrong, can anyone help me?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Chat_Application
{
class Server
{
private Socket serverSocket = null;
private volatile ArrayList connections = null; // will hold all client sockets
private const int port = 1090;
private IPAddress ipAddress = null;
private IPEndPoint ipEndPoint = null;
private Thread listenThread = null; // seperate thread to run the server
private ManualResetEvent allDone = null;
public Server()
{
this.serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.connections = new ArrayList();
ipAddress = IPAddress.Parse(GetLocalIPv4(NetworkInterfaceType.Ethernet));
ipEndPoint = new IPEndPoint(ipAddress, port);
listenThread = new Thread(StartListen);
allDone = new ManualResetEvent(false);
}
public void Start()
{
listenThread.Start();
}
public void StartListen()
{
this.serverSocket.Bind(ipEndPoint);
this.serverSocket.Listen(20);
Program.mainWin.console.Text += "\n<INFO> Socket bound, listening for connections...";
while (true)
{
allDone.Reset();
serverSocket.BeginAccept(new AsyncCallback(AcceptConnectionAsync), serverSocket);
Program.mainWin.console.Text += "\n<INFO> Conncetion accepted...";
allDone.WaitOne();
}
}
public void AcceptConnectionAsync(IAsyncResult AR)
{
Byte[] bufferBytes = new byte[1024];
allDone.Set();
Socket client = (Socket) AR.AsyncState;
int x = client.Receive(bufferBytes);
Program.mainWin.console.Text += System.Text.Encoding.Default.GetString(bufferBytes);
}
public string GetLocalIPv4(NetworkInterfaceType _type)
{
string output = "";
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
{
if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
{
foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
output = ip.Address.ToString();
}
}
}
}
return output;
}
}
}
You're never calling EndAccept (from the example you linked):
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar); // This right here
The socket in ar.AsyncState is the listening socket, not the connected client. AsyncState is an arbitrary object you can use to pass information to the callback method (AcceptConnectionAsync). In this case, you are passing the serverSocket (the second parameter below):
serverSocket.BeginAccept(new AsyncCallback(AcceptConnectionAsync), serverSocket);
When you call EndAccept on the listening socket, you are getting a new Socket instance that is a specific connection to the client -- your listener socket will start the asynchronous request to accept another connection in your while loop in StartListen. The socket returned by EndAccept is in a connected state and ready to communicate with the other endpoint, based on this specific callback invocation (hence, the requirement to supply IAsyncResult as an argument).
This is referred to as the Asynchronous Programming Model. MSDN has some great information on this (as usual).

Detect SQL event with C# application

I have the code below (a literal copy/paste from MSDN website just to be clear), which seems to connect as it should (except for an "Access Denied" error which is okay because my security requests haven't gone through yet). What I need to do is detect when our sql server has executed an insert or update operation. Basically this application should be running 24/7 and perform certain functions when an operation as such comes across the listener. I'm not asking for the code to be laid out in front of me, but I am asking where to start. This is something I have no clue how to do at this point and am being told I have roughly a week to figure it out and get it done. Can someone point me in the right direction? Thank you in advance!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Connect_Server
{
class Program
{
static string output = "";
static void Main(string[] args)
{
createListener();
}
static public void createListener()
{
// Create an instance of the TcpListener class.
TcpListener tcpListener = null;
IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
try
{
// Set the listener on the local IP address
// and specify the port.
tcpListener = new TcpListener(ipAddress, 80);
tcpListener.Start();
output = "Waiting for a connection...";
}
catch (Exception e)
{
output = "Error: " + e.ToString();
Console.Write(output);
}
while (true)
{
// Always use a Sleep call in a while(true) loop
// to avoid locking up your CPU.
Thread.Sleep(10);
// Create a TCP socket.
// If you ran this server on the desktop, you could use
// Socket socket = tcpListener.AcceptSocket()
// for greater flexibility.
TcpClient tcpClient = tcpListener.AcceptTcpClient();
// Read the data stream from the client.
byte[] bytes = new byte[256];
NetworkStream stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
SocketHelper helper = new SocketHelper();
helper.processMsg(tcpClient, stream, bytes);
}
}
}
}
Check out SQL Server Notifications. This will send a signal to your app when some underlying dataset changes. I'm not sure how heavy this is for the server, so if you have a large number of clients waiting for a notification you should load-test it carefully....

How to prevent asynchronous method form processing?

I have this problem where, one part of the code process faster than it was meant to. i some situations when i call client.BeginConnect and it is connected before any other code is treated.
example:
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
// this code below is preformed later because ConnectCallback is called to quickly
Debug.WriteLine(client.LocalEndPoint.ToString());
Problem here is that method ConnectCallback is sometimes performed faster than the code Debug.Writeline(....)
I only need to block or enable ConnectCallback function so Debug.Writeline would perform faster than ConnectCallback.
Thanks for help.
Here is a great MSDN article: Using an Asynchronous Client Socket:
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
So, just move the Debug.WriteLine() call into your ConnectCallback() method.

TcpConnection capable of handling concurrent requests. Client-Server

I'm trying to build a simple Client-Server Application with the following codes:
//SERVER
IPAddress ipAd = IPAddress.Parse("192.163.10.101");
TcpListener myList = new TcpListener(ipAd, 8001);
myList.Start();
Console.WriteLine("The server is running at port 8001...");
Console.WriteLine("The local End point is :" + myList.LocalEndpoint);
Console.WriteLine("Waiting for a connection.....");
Socket s = myList.AcceptSocket();
Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);
//CLIENT
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("192.163.10.101",8001);
Console.WriteLine("Connected");
this actually does what I need wherein the client can connect to the server. However, when I try to run multiple instances of the client to connect with the server, the server only accepts the first client to connect. Meaning there's like a one-to-one connection wherein only one client can connect with the client. However, what I need is to give the server the ability to accept connections from more than one client.
If anyone can point me a possible solution to this, I'll really be appreciative! Thanks!
You need to call AcceptSocket again to accept another socket.
A typical design would be to have to call BeginAcceptSocket and in the callback call EndAcceptSocket, dispatch the client processing to its own thread (or a worker thread using async methods) and then call BeginAcceptSocket again.
This fragment is untested but should be more or less right/ get you thinking in the right direction.
class Server
{
public Server()
{
TcpListener listener = null;
// init the listener
listener.BeginAcceptSocket((ar) => AcceptLoop(ar, listener),null);
}
public void HandleClientSocketRead(IAsyncResult ar, byte[] recvBuffer, Socket clientSocket)
{
int recvd = clientSocket.EndReceive(ar);
//do something with the data
clientSocket.BeginReceive(recvBuffer, 0, 1024, SocketFlags.None, (ar2) => HandleClientSocketRead(ar2, recvBuffer, clientSocket), null);
}
public void AcceptLoop(IAsyncResult ar, TcpListener listener)
{
Socket clientSocket = listener.EndAcceptSocket(ar); // note that this can throw
byte[] recvBuffer = new byte[1024];
clientSocket.BeginReceive(recvBuffer, 0, 1024, SocketFlags.None, (ar2) => HandleClientSocketRead(ar2, recvBuffer, clientSocket), null);
listener.BeginAcceptSocket((ar) => AcceptLoop(ar, listener), null);
}
}
If you are looking to write a server, a good design is to have a [server].exe and a [client].exe. The [server].exe, will of course accept and process all incoming connections, maintain the client sockets, and perform whatever actions you need. Below is a very basic example on writing a server to accept multiple client sockets, and store them in a List object. This, however, is not multithreaded so the code, does block.
[server].exe
//-----------------------------------------------------------------------------
// <copyright file="Program.cs" company="DCOM Productions">
// Copyright (c) DCOM Productions. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
namespace MultiSocketServerExample {
using System;
using System.Net.Sockets;
using System.Net;
using System.Collections.Generic;
class Program {
static List<Socket> m_ConnectedClients = new List<Socket>();
static void Main(string[] args) {
Socket host = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
host.Bind(new IPEndPoint(IPAddress.Any, 9999));
host.Listen(1);
while (true) {
m_ConnectedClients.Add(host.Accept());
Console.WriteLine("A client connected.");
}
}
}
}
Then to work with your clients: (Again, very basic example)
m_ConnectedClients[0].Send(Encoding.ASCII.GetBytes("hello!");
Network programming with the Socket class is a lot easier in my opinion then using TcpListener and TcpClient. The reason I say this is that it is already a really good and easy to use implementation, and by using TcpListener and TcpClient where they create further abstraction, lessenes your ability to understand what is going on (in my opinion).

Categories

Resources