So I am busy writing an Watchdog for message queue installed on one of my servers. I have a application ( 3rd party ) that is listening on the queue and processing the messages. I now want to do a count on it and if message reach example 1500 I send a email. So all my code works except that I need to close the 3rd party app to use the message queue. What I do is I get all the queue names that exist. Work fine.
public void GetPrivateQueues()
{
MessageQueue[] QueueList =
MessageQueue.GetPrivateQueuesByMachine(".");
foreach (MessageQueue queueItem in QueueList)
{
i++;
myPrivateQueues.Add(queueItem.Path);
Count(queueItem.Path);
}
return;
}
So when I do the count of the queue like this
public void Count(String path)
{
MessageQueue queue = new MessageQueue(path);
MessageEnumerator messageEnumerator = queue.GetMessageEnumerator2();
int iii = 0;
while (messageEnumerator.MoveNext())
{
iii++;
}
myPrivateQueuesCount.Add(iii);
return;//i;
}
I get the error.
System.Messaging.MessageQueueException (0x80004005): Sharing violation resulted from queue being open already for exclusive receive.
How can I go about reading the queue to do a count without trying to get exclusive access on it. I just need to count it.
Thank you
Regards
I used performance Counter to read the queue.
Working like a dream now!
Added the catch exception. This is for when the queue is blank. I write a 0. Performance counter gives error on blank queue.
public void Count(String path)
{
path = path.Remove(0, 21);
try
{
PerformanceCounter queueCounter = new PerformanceCounter(
"MSMQ Queue",
"Messages in Queue",
#path);
Console.WriteLine("Queue contains {0} messages",
queueCounter.NextValue().ToString());
myPrivateQueuesCount.Add((int)queueCounter.NextValue());
}
catch (Exception exc)
{
myPrivateQueuesCount.Add(0);
}
return;
}
I am trying out Azure Service Bus queue. I have the below code:
Queue send:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (!namespaceManager.QueueExists("Test"))
{
QueueDescription qD = new QueueDescription("Test");
qD.DefaultMessageTimeToLive = new TimeSpan(05, 00, 00);
qD.LockDuration = new TimeSpan(00, 02, 30);
qD.MaxSizeInMegabytes = 5120;
namespaceManager.CreateQueue(qD);
}
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test", ReceiveMode.PeekLock);
var qMessage = Console.ReadLine();
using (MemoryStream strm = new MemoryStream(Encoding.UTF8.GetBytes(qMessage)))
{
BrokeredMessage bMsg = new BrokeredMessage(strm);
bMsg.MessageId = Guid.NewGuid().ToString();
bMsg.TimeToLive = new TimeSpan(05, 00, 00);
client.Send(bMsg);
Console.WriteLine("Message sent");
}
}
Console.ReadLine();
The receive code:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test",ReceiveMode.PeekLock);
if (client != null)
{
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromSeconds(31);
client.OnMessage((message) =>
{
Console.WriteLine(message.State.ToString());
Console.WriteLine("Message Id: " + message.MessageId);
Stream stream = message.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
Console.WriteLine("Message: " + reader.ReadToEnd());
Console.WriteLine("***************");
message.Abandon();
});
Console.ReadLine();
}
}
I see that whenever I call Abandon, the message is getting DeadLettered. My assumption was that it should get Active and can be picked up by another client.
Your understanding of BrokeredMessage.Abandon Api is correct. It is intended to abandon the peek-lock acquired on the message (but NOT abandon the message itself) and hence, makes it available for other receivers to pick the Message up.
Here's how we envisioned different states of a peek-lock'ed message:
Basics first
The 'Why': If Customers need Competing-Consumer (Job-Queue) semantics - where they need multiple workers to simultaneously process different messages from a Queue with Exactly-Once guarantee - then they use the ReceiveMode.PeekLock. In this model, each worker (the queue receiver) needs a way to communicate the Progress of its Current message (Job) to other workers. Hence, brokeredMessage provides 4 functions to express the states.
The 'What':
if a message is successfully processed by the current Worker - call BrokeredMessage.Complete()
if the BrokeredMessage cannot be processed by the current worker, and want the processing to be retried on another Worker - then, Abandon the message. But, the catch here is: lets say, there are 2 workers and each of them thinks that the other one can process this message and calls Abandon - soon they will end up in an Infinite loop of retry'ing to process just that message! So, to avoid this situation, we provided a Configuration called MaxDeliveryCount on QueueDescription. This setting guards the limit on the number of times the message is delivered from the Queue to receiver. In the above example, Each time you received (and abandoned) the message, the 'deliveryCount' on the ServiceBus service is incremented. When it reaches 10 - the message has hit max no. of deliveries and hence, will be deadlettered.
if the current receiver (worker) knows for sure, that, this message cannot be processed, BrokeredMessage.DeadLetter(). The goal here is to let the consuming application Audit the dead-lettered messages regularly.
if the current receiver (worker) cannot process this message, but, knows that this message can be processed at a later point of time BrokeredMessage.Defer().
HTH!
Sree
I try to get data asynchronously from a serial port. Mainly because DataReceived event seems not to be reliable enough and we end up with RXOver errors.
Target is .NET 4.0, so we need to use the older Begin/End methods.
I also learned that it is needed to call BeginRead() within a thread from the ThreadPool, otherwise the initiating thread would have terminated already when processing the passed callback.
However, even with a pooled thread I always get an IOException "The I/O operation has been aborted because of either a thread exit or an application request".
The underlying com port is open.
Any advise welcome.
#region DataReceiving
private readonly byte[] buffer = new byte[MAX_BUFFER_SIZE];
private void StartDataReceiving()
{
ThreadPool.QueueUserWorkItem( state => this.AsyncDataReceiving() );
}
private void AsyncDataReceiving()
{
this.serialPort.BaseStream.BeginRead(
this.buffer, 0, this.buffer.Length,
asyncResult =>
{
try
{
int actualLength = this.serialPort.BaseStream.EndRead( asyncResult );
byte[] received = new byte[actualLength];
Buffer.BlockCopy( this.buffer, 0, received, 0, actualLength );
this.dataConsumer.Add( received );
}
catch (IOException ex)
{
this.HandleSerialError( ex );
}
// Setup receiving action again
this.AsyncDataReceiving();
}
, null );
}
#endregion
I'm working on a game in Unity with C#, and using TCP to exchange data between the server and the client.
I'm using async calls to connect and that seems to work fine. Then a handshake/auth happens as the server prompts the client for client version, client replies, server asks for player's name, player replies, and if all good, server notifies client has been accepted, otherwise connection is closed.
That works most of the time. However, in about 1 in 20, on the very first msg (server->client request for client version, and within a few seconds of game start), the async callback from the first BeginSend never gets called, thus halting the auth process. The client does receive the message based on the logging I have in place, and verified via debugging.
The server's call is:
m_isSending = true;
m_socket.BeginSend(byteArray.buffer, 0, byteArray.arrayLen, SocketFlags.None, new AsyncCallback(EndAsyncWrite), byteArray);
I've added m_isSending to help more easily debug/track whether a message is being sent to confirm if the callback has been called.
with EndAsyncWrite being:
protected void EndAsyncWrite(IAsyncResult iar)
{
m_isSending = false;
m_socket.EndSend(iar);
ByteArray byteArray = (iar.AsyncState as ByteArray);
//Add prev msg's ByteArray to await recycling
lock (m_byteArraysAwaitingRecycle)
{
m_byteArraysAwaitingRecycle.Add(byteArray);
}
}
In those 1 in 20 situations, m_isSending will still be true even after the client has received and processed the message. I've poked around in the debugger, but due to, what I assume is Unity's utilizing of mono, can't peer too far in. I have however found the message in the socket's writeQ (which I assume is the queue for writing). Normally it is empty, so I'm wondering if it might be able to shed some light into why the callback isn't being called.
Watch Snippet. The only entry present is the message that was sent and the client received.
Other info: nagle's is disable, blocking is set to true. The other 19/20 tries seem to work alright, with no changes. I'm currently testing with localhost as destination.
So I'm pretty confused as I'm doing everything properly as far as I can tell. Why wouldn't the callback get called? Any ideas? Any suggestions? Is there any way to work around this?
After doing some more testing, I've come to the conclusion it's a bug/effect on threads while running in Unity Editor. Here's how I came to that conclusion:
I noticed the problem occurred more often the first time opening the Unity project and starting the game. Stopping, and then starting, usually would work. It'd always work within 2-3 tries and continue to work for at least another dozen or so attempts. That could of course indicate some kind of race condition or threading concurrency issue in my own code, so I did the following: 1) Made a very stripped down tcplistener/tcpclient project in Unity, eliminating any caching/recycling of byte[] arrays or other things that may inadvertently affect the async or overall performance. 2) I tested this new project both in editor, and as a standalone build to check for outcome.
This of course requires unity, though it could probably just as easily be adopted to a .net console application for further evaluation. The project consisted of one scene, with a game camera the following three scripts attached to the camera. You need to drag/drop the TCPClient and TCPServer references onto ConnectGUI once attached to the gameobject. The code:
ConnectGUI.cs
using UnityEngine;
using System.Collections;
public class ConnectGUI : MonoBehaviour {
public enum ConnectionState
{
NotConnected,
AttemptingConnect,
Connected
}
public TCPClient client;
public TCPServer server;
// Use this for initialization
void Start ()
{
client.connectState = ConnectionState.NotConnected;
}
// Update is called once per frame
void Update () {
}
void OnGUI()
{
GUI.Label(new Rect(10, 10, Screen.width - 20, 20), client.connectState.ToString());
if (client.connectState == ConnectionState.NotConnected)
{
if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.5f - 40, 400, 80), "Connect"))
{
server.StartServer();
System.Threading.Thread.Sleep(10);
client.StartConnect();
}
}
}
}
TCPClient.cs
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
public class TCPClient : MonoBehaviour {
public ConnectGUI.ConnectionState connectState;
Socket m_clientSocket;
byte[] m_readBuffer;
void Start()
{
connectState = ConnectGUI.ConnectionState.NotConnected;
m_readBuffer = new byte[1024];
}
public void StartConnect()
{
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
System.IAsyncResult result = m_clientSocket.BeginConnect("127.0.0.1", 10000, EndConnect, null);
bool connectSuccess = result.AsyncWaitHandle.WaitOne(System.TimeSpan.FromSeconds(10));
if (!connectSuccess)
{
m_clientSocket.Close();
Debug.LogError(string.Format("Client unable to connect. Failed"));
}
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("Client exception on beginconnect: {0}", ex.Message));
}
connectState = ConnectGUI.ConnectionState.AttemptingConnect;
}
void EndConnect(System.IAsyncResult iar)
{
m_clientSocket.EndConnect(iar);
m_clientSocket.NoDelay = true;
connectState = ConnectGUI.ConnectionState.Connected;
BeginReceiveData();
Debug.Log("Client connected");
}
void OnDestroy()
{
if (m_clientSocket != null)
{
m_clientSocket.Close();
m_clientSocket = null;
}
}
void BeginReceiveData()
{
m_clientSocket.BeginReceive(m_readBuffer, 0, m_readBuffer.Length, SocketFlags.None, EndReceiveData, null);
}
void EndReceiveData(System.IAsyncResult iar)
{
int numBytesReceived = m_clientSocket.EndReceive(iar);
ProcessData(numBytesReceived);
BeginReceiveData();
}
void ProcessData(int numBytesRecv)
{
string temp = TCPServer.CompileBytesIntoString(m_readBuffer, numBytesRecv);
Debug.Log(string.Format("Client recv: '{0}'", temp));
byte[] replyMsg = new byte[m_readBuffer.Length];
System.Buffer.BlockCopy(m_readBuffer, 0, replyMsg, 0, numBytesRecv);
//Increment first byte and send it back
replyMsg[0] = (byte)((int)replyMsg[0] + 1);
SendReply(replyMsg, numBytesRecv);
}
void SendReply(byte[] msgArray, int len)
{
string temp = TCPServer.CompileBytesIntoString(msgArray, len);
Debug.Log(string.Format("Client sending: len: {1} '{0}'", temp, len));
m_clientSocket.BeginSend(msgArray, 0, len, SocketFlags.None, EndSend, msgArray);
}
void EndSend(System.IAsyncResult iar)
{
m_clientSocket.EndSend(iar);
byte[] msg = (iar.AsyncState as byte[]);
string temp = TCPServer.CompileBytesIntoString(msg, msg.Length);
Debug.Log(string.Format("Client sent: '{0}'", temp));
System.Array.Clear(msg, 0, msg.Length);
msg = null;
}
}
TCPServer.cs
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
public class TCPServer : MonoBehaviour
{
public enum TestMessageOrder
{
NotConnected,
Connected,
SendFirstMessage,
ReceiveFirstMessageReply,
SendSecondMessage,
ReceiveSecondMessageReply,
SendThirdMessage,
ReceiveThirdMessageReply,
Error,
Done
}
protected TcpListener m_tcpListener;
protected Socket m_testClientSocket;
protected byte[] m_readBuffer;
[SerializeField]
protected TestMessageOrder m_testClientState;
public void StartServer()
{
m_tcpListener = new TcpListener(IPAddress.Any, 10000);
m_tcpListener.Start();
StartListeningForConnections();
}
void StartListeningForConnections()
{
m_tcpListener.BeginAcceptSocket(AcceptNewSocket, m_tcpListener);
Debug.Log("SERVER ACCEPTING NEW CLIENTS");
}
void AcceptNewSocket(System.IAsyncResult iar)
{
m_testClientSocket = null;
m_testClientState = TestMessageOrder.NotConnected;
m_readBuffer = new byte[1024];
try
{
m_testClientSocket = m_tcpListener.EndAcceptSocket(iar);
}
catch (System.Exception ex)
{
//Debug.LogError(string.Format("Exception on new socket: {0}", ex.Message));
}
m_testClientSocket.NoDelay = true;
m_testClientState = TestMessageOrder.Connected;
BeginReceiveData();
SendTestData();
StartListeningForConnections();
}
void SendTestData()
{
Debug.Log(string.Format("Server: Client state: {0}", m_testClientState));
switch (m_testClientState)
{
case TestMessageOrder.Connected:
SendMessageOne();
break;
//case TestMessageOrder.SendFirstMessage:
//break;
case TestMessageOrder.ReceiveFirstMessageReply:
SendMessageTwo();
break;
//case TestMessageOrder.SendSecondMessage:
//break;
case TestMessageOrder.ReceiveSecondMessageReply:
SendMessageTwo();
break;
case TestMessageOrder.SendThirdMessage:
break;
case TestMessageOrder.ReceiveThirdMessageReply:
m_testClientState = TestMessageOrder.Done;
Debug.Log("ALL DONE");
break;
case TestMessageOrder.Done:
break;
default:
Debug.LogError("Server shouldn't be here");
break;
}
}
void SendMessageOne()
{
m_testClientState = TestMessageOrder.SendFirstMessage;
byte[] newMsg = new byte[] { 1, 100, 101, 102, 103, 104 };
SendMessage(newMsg);
}
void SendMessageTwo()
{
m_testClientState = TestMessageOrder.SendSecondMessage;
byte[] newMsg = new byte[] { 3, 100, 101, 102, 103, 104, 105, 106 };
SendMessage(newMsg);
}
void SendMessageThree()
{
m_testClientState = TestMessageOrder.SendThirdMessage;
byte[] newMsg = new byte[] { 5, 100, 101, 102, 103, 104, 105, 106, 107, 108 };
SendMessage(newMsg);
}
void SendMessage(byte[] msg)
{
string temp = TCPServer.CompileBytesIntoString(msg);
Debug.Log(string.Format("Server sending: '{0}'", temp));
m_testClientSocket.BeginSend(msg, 0, msg.Length, SocketFlags.None, EndSend, msg);
}
void EndSend(System.IAsyncResult iar)
{
m_testClientSocket.EndSend(iar);
byte[] msgSent = (iar.AsyncState as byte[]);
string temp = CompileBytesIntoString(msgSent);
Debug.Log(string.Format("Server sent: '{0}'", temp));
}
void BeginReceiveData()
{
m_testClientSocket.BeginReceive(m_readBuffer, 0, m_readBuffer.Length, SocketFlags.None, EndReceiveData, null);
}
void EndReceiveData(System.IAsyncResult iar)
{
int numBytesReceived = m_testClientSocket.EndReceive(iar);
ProcessData(numBytesReceived);
BeginReceiveData();
}
void ProcessData(int numBytesRecv)
{
string temp = TCPServer.CompileBytesIntoString(m_readBuffer, numBytesRecv);
Debug.Log(string.Format("Server recv: '{0}'", temp));
byte firstByte = m_readBuffer[0];
switch (firstByte)
{
case 1:
Debug.LogError(string.Format("Server should not receive first byte of 1"));
m_testClientState = TestMessageOrder.Error;
break;
case 2:
m_testClientState = TestMessageOrder.ReceiveSecondMessageReply;
break;
case 3:
Debug.LogError(string.Format("Server should not receive first byte of 3"));
m_testClientState = TestMessageOrder.Error;
break;
case 4:
m_testClientState = TestMessageOrder.ReceiveThirdMessageReply;
break;
case 5:
Debug.LogError(string.Format("Server should not receive first byte of 5"));
m_testClientState = TestMessageOrder.Error;
break;
default:
Debug.LogError(string.Format("Server should not receive first byte of {0}", firstByte));
m_testClientState = TestMessageOrder.Error;
break;
}
SendTestData();
}
void OnDestroy()
{
if (m_testClientSocket != null)
{
m_testClientSocket.Close();
m_testClientSocket = null;
}
if (m_tcpListener != null)
{
m_tcpListener.Stop();
m_tcpListener = null;
}
}
public static string CompileBytesIntoString(byte[] msg, int len = -1)
{
string temp = "";
int count = len;
if (count < 1)
{
count = msg.Length;
}
for (int i = 0; i < count; i++)
{
temp += string.Format("{0} ", msg[i]);
}
return temp;
}
}
What this does is start's a TcpListener, and begins an async connection socket accept. Then a client socket is created and connects as a tcp socket (on port 10000, of 127.0.0.1). It turns of nagle's algorithm and the server sends a first message. The client receives the message, increments the first byte from 1->2 and returns the original message. Server then receives that message, and sends another message starting with 3. Client receives, increments 3->4 and echos back the rest of the message. Server then receives that, and sends a 3rd and last message starting with 5. Client turns 5->6 and sends back message. Once that occurs, the server prints "ALL DONE". Both server and client should print to log the various message contents (not always in the same order due to the nature of threading).
If for some reason "ALL DONE" is not printed, then the experiment has failed.
Running this in Unity Editor, it failed 10/10 on the first run, when immediately run after opening the editor. Subsequent attempts to run it resulted in mixed success for the 2nd and 3rd attempts. By the 4th attempt, I have no recorded failures.
I then compiled the project as a standalone program, and repeated the same number of attempts. Since it was reliant on the "ALL DONE" in the log, output.log was checked for "ALL DONE" and was found each time.
So, unless I'm misinterpretating the results, there is a problem either in Unity Editor's or its underlying mono version that is mucking about with threads which causes tcp async read/writes to fail in some capacity. However in the standalone builds, whatever that something is, thankfully, does not seem to appear to be a problem at least as far as testing on Windows allows.
I fully admit the testing was limited with only about 40 runs each but the results were significantly different, though I am too lazy to calculate actual significance. I am puzzled and a still bit concerned that it may be my own flawed implementation, since something like this is not more widespread; however Unity's own networking relies mainly on RPC calls, and most middleware fully embrace an exclusive UDP based network option.
If there is some fundamental flaw present, please let me know, otherwise I hope this may help some lost soul (as I was for almost two weeks) in the future as there is little to no searchable results on this topic. This was all done in Unity 4.6.1f1, but also tested by a friend in the present Unity 5 Beta (unsure of current beta version number).
Personally, while this is extremely annoying, I feel I can ignore this as an editor-only problem with little-to-no potential to impact actual players playing a compiled version. It will be something to heavily test once builds are regularly happening.
I have written the code for Server/Client program in C#. Without using the Thread, it is working fine. If I use Thread, I am getting following error message.
The thread '' (0x9a8) has exited with code 0 (0x0).
Sample code
public class MyServer{
public MyServer (){
...
...
System.Threading.Thread socThread = new System.Threading.Thread(new System.Threading.ThreadStart(receiveSockets));
socThread.Start();
}
private void receiveSockets()
{
try
{
while(true){
IPAddress ipadd = IPAddress.Parse(systemIPAddress);
TcpListener tcp = new TcpListener(ipadd, 8001);
tcp.Start();
Console.WriteLine("Waiting for client in 8001 port no");
Socket socket = tcp.AcceptSocket();
Console.WriteLine("Client address : " + socket.RemoteEndPoint);
System.Threading.Thread socThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(receiveData));
socThread.Start(socket);
}
}
catch (Exception e)
{
Console.WriteLine("Error in receive Sockets : " + e.Message);
}
}
private void receiveData(object obj)
{
try
{
while(true){
Socket socket = (Socket)obj;
byte[] data = new byte[1000];
int status = socket.Receive(data);
Console.WriteLine("Received 1.");
string content = Encoding.UTF8.GetString(data, 0, 1000);
Console.WriteLine("Received data 1 : " + content);
}
}
catch (Exception e)
{
Console.WriteLine("Error in receive Data : " + e.Message);
}
}
static void Main(string[] args){
MyServer server = new MyServer();
}
Client Program
static void Main(string[] args)
{
TcpClient tcp = new TcpClient();
tcp.Connect("192.168.1.11", 8001);
Stream stream = tcp.GetStream();
String msg = "Testing...";
byte[] content = new byte[msg.Length * sizeof(char)];
System.Buffer.BlockCopy(msg.ToCharArray(), 0, content, 0, content.Length);
stream.Write(content, 0, content.Length);
}
I am getting the following output.
IP Addrress : 192.168.1.11
Waiting for client in 8001 port no
Client address : 192.168.1.11:50140
The thread '' (0x9a8) has exited with code 0 (0x0).
A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
The thread '' (0x1760) has exited with code 0 (0x0).
Error in receive Data : An existing connection was forcibly closed by the remote host
The program '[1396] Window_Server.vshost.exe: Managed (v4.0.30319)' has exited with code 0 (0x0).
Please help me to fix this issue.
You need to figure out why you are throwing a socket exception. If you read the documentation for Socket.Receive You would see this section:
Note
If you receive a SocketException, use the SocketException.ErrorCode property to obtain the specific error code.
After you have obtained this code, refer to the Windows Sockets
version 2 API error code documentation in the MSDN library for a
detailed description of the error.
Follwing that link shows you how to read the error code:
The ErrorCode property contains the error code that is associated with
the error that caused the exception.
The default constructor for SocketException sets the ErrorCode
property to the last operating system error that occurred. For more
information about socket error codes, see the Windows Sockets version
2 API error code documentation in MSDN.
Which should bring you to the error codes page.
Now depending on your error code, which you have not provided, you can diagnose the network issue.
"The thread '' (0x9a8) has exited with code 0 (0x0)." is not an error. It is simply telling you that a background thread has exited. Zero means the thread ran and exited successfully.
The exception is in receiveData(object obj) as you should be able to tell, given the exception , "Error in receive Data : An existing connection was forcibly closed by the remote host".
If you post the client program you are working with I might be able to help.
The problem is that Main() does not wait for the sockets to be done with their jobs. As soon as it has created the threads, it exists... And the threads are destroyed.
You need to wait for the socket-handling threads by using events of some sort, or by sleeping, from Main() - or from MyServer(), as long as the program exists only when the whole job is done.