I tried to send form a single UdpClient to several different open UDP sockets from localhost to localhost. However in the first version only the first message of the loop was sent, the rest did not even make it out.
When trying to isolate the error the second call to udpClient.Connect() raises a SocketException pointing to the Error WSAEISCONN 10056
The official documentation of UdpClient.Connect() here states in the remark section if you want to send to different endpoints, call Connect again. However this is the opposite of what the error tells me.
So is this just an error in the docs of the Connect methode, or do I miss something here?
Too fast requests should not be an issue with only 3 requests every 3 Seconds and as I use the normal Send(buffer) call, there should be no packets waiting to be sent to the previous endpoint.
Simple example to reproduce (I used .net 6, Win10):
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace UdpClientIssue;
internal class Program
{
static void Main(string[] args)
{
var sender = CreateSender(3_000, 40001, 40002, 40003);
var reciverA = CreateReciver("ReciverA", 40001);
var reciverB = CreateReciver("ReciverB", 40002);
var reciverC = CreateReciver("ReciverC", 40003);
reciverA.Start();
reciverB.Start();
reciverC.Start();
sender.Start();
sender.Join();
}
static void Reciver(int port)
{
var localEP = new IPEndPoint(IPAddress.Loopback, port);
using var listener = new UdpClient(localEP);
var buff = new byte[1024];
while (true)
{
var senderEP = new IPEndPoint(IPAddress.None, 0);
var data = listener.Receive(ref senderEP);
var message = Encoding.UTF8.GetString(data);
Console.WriteLine($"Recived at {port}: {message}");
}
}
static void Sender(int interval_ms, params int[] ports)
{
int messageNumber = 0;
while (true)
{
Thread.Sleep(interval_ms);
using (var udpClient = new UdpClient())
{
foreach (var remotePort in ports)
{
var message = $"Message {++messageNumber} to {remotePort}";
var sendBuffer = Encoding.UTF8.GetBytes(message);
Console.WriteLine($"Sending to {remotePort}: {message}");
var remoteEP = new IPEndPoint(IPAddress.Loopback, remotePort);
//This errors on second iteration
//udpClient.Connect(remoteEP);
//udpClient.Send(sendBuffer);
//This works
udpClient.Send(sendBuffer, remoteEP);
}
}
messageNumber += 100 - ports.Length;
}
}
static Thread CreateReciver(string name, int port)
{
var ts = new ThreadStart(() => Reciver(port));
var t = new Thread(ts)
{
Name = name
};
return t;
}
static Thread CreateSender(int interval_ms, params int[] ports)
{
var ts = new ThreadStart(() => Sender(interval_ms, ports));
var t = new Thread(ts)
{
Name = "Sender"
};
return t;
}
}
I believe it's error in the docs. Description of this error message says:
Socket is already connected.
A connect request was made on an already-connected socket
And source code explicitly throws this exception with this error code if socket was already connected.
Here you see it just forwards connect to Socket:
public void Connect(IPEndPoint endPoint)
{
ThrowIfDisposed();
ArgumentNullException.ThrowIfNull(endPoint);
CheckForBroadcast(endPoint.Address);
Client.Connect(endPoint);
_active = true;
}
And in Socket.Connect you see it throws this errors if socket is already connected:
if (_isConnected)
{
throw new SocketException((int)SocketError.IsConnected);
}
Fortunately you already know the better way of doing this - just pass remote endpoint to Send call. UDP is connectionless protocol anyway so calling Connect only sets default remote endpoint and doesn't perform actual connection.
Related
I am having a weird problem where I am making a chat connection with TCPListeners and sockets. For some reason when a client "connects" to the server it will show they have connected twice. Also what is weird once all of the users have connected I will send out a message to all of them. They will respond back with it acknowledged and state that their chat has started.
Things I am noticing with how i have it setup:
It appears according to the log that the user "connects" twice the second "connect occurs once it hits the white(true) loop.
When they send over the acknowledgement back to the server not all of the data is getting sent over. If I do a thread sleep on the client it does appear to start working but it is still inconsistent.
Here is the code:
Server:
private TcpListener tcpListener;
private Thread listen;
private TcpListener tcpUser1, tcpUser2,tcpUser3;
NetworkStream User1Stream,User2Stream,User3Stream;
public event NetworkMessageStringDelegate MessageFromUser;
TcpClient client;
public void start(string ip){
IpHostEntry host = dns.GetHostEntry(dns.GetHostName());
IpAddress[] ip = host.AddressList;
serverStatus = "Server started with IP of: " + ip;
Thread.Sleep(1);
tcpUser1 = new TcpListener(IpAddress.Any, 4001);
listen = new Thread(new ThreadStart(() => ListenUser1(tcpUser1)));
listen.Start();
Thread.Sleep(1);
tcpUser2 = new TcpListener(IpAddress.Any, 4002);
listen = new Thread(new ThreadStart(() => ListenUser2(tcpUser2)));
listen.Start();
Thread.Sleep(1);
tcpUser3 = new TcpListener(IpAddress.Any, 4003);
listen = new Thread(new ThreadStart(() => ListenUser3(tcpUser3)));
listen.Start();
Thread.Sleep(1);
}
public void ListenUser3(TcpListener tmp){
tcpListener = (TcpListener)tmp;
Socket = "Listening for User3";
tcpUser3.Start();
Thread.Sleep(2);
while(true){
user3 = this.tcpListener.AcceptTcpClient();
Thread user3Thread = new Thread(new ParmeterizedThreadStart(receiveUser3Data));
user3Thread.Start(user3);
}
}
//Mostly from MS documenation
private void receiveUser3Data(object client){
client = (TcpClient)client;
User3Stream = client.getStream();
Socket = "Connected to User: " + client.Client.RemoteEndPoint.toString();
byte[] message = new byte[4096];
int total;
//This is the line it will display the socket message Twice. "Connected to User...."
while(true){
total = 0;
try{
do{
total = User3Stream.Read(message,0,4096);
}
while(user3.DataAvailable);
}
catch()
{
Socket = "Error State";
}
}
byte[] infoPacket = new byte[total];
Array.ConstrainedCopy(message,0,infoPacket,total);
if(MessageFromUser3 != null){
MessageFromUser?.Invoke(packet);
}
}
Client:
public void ConfigureUser3(){
try{
socket = new Network.TCPIPClient();
socket.ReceiveMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(MessageFromserver);
socket.SendMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(sendMessage);
userThread = new Thread(() => socket.Start("0.0.0.0),4054));
userThread.Start();
}
catch(Exception ex){
}
}
//This is where if I sleep it will send but it is still inconsident
private void SendMEssageToSever(object tmpVal){
object[] sendMessage = tmpVal as object[];
string tmpSendValue = tmpVal[0].ToString();
byte sendValue = Coonvert.ToByte(tmpVal[1]);
packetData[0] = 0;
packetData[1] = sendValue;
packetData[2] = Convert.ToByte(tmpSendValue);
socket.sendMessage = packetData;
}
private voide sendMessage(byte[] userMessage){
try{
if(socket){
outputMessage.Enqueue(userMessage);
while(outputMessage.Count > 0){
Byte[] sendMessage = outputMessage.Dequeue();
string message = ascII.GetString(sendMessage);
if(socket.Connected){
lock(socket){
socket.Send(sendMessage,sendMessage.length,0);
}
}
}
}
}
catch(Exception ex)
}
This code is essentially repeated for all users that are connected to the server.
The TcpListener has asynchronous methods like BeginAcceptTcpClient.
TcpClient.GetStream() (which is a NetworkStream) also has asynchronous methods like BeginRead.
I suggest you change your server code to use these and to store the user state in a class and pass this class to and fro between Begin* and End* methods.
You can support N number of users then, and don't have to repeat code for each user. You also don't have to have 3 different listeners for 3 connections. Have just one listener and accept clients over this one. The rest is two-way communication via TcpClient.GetStream()
Here is a minimal server example which listens on port 9988 (for only LoopBack, which means the local machine). You can of course change this.
There is no client example here. Only the server. Just copy/paste the code into your program.cs file in a console application.
I hope the comments are sufficient to explain the code.
I hope also, that this helps.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
class Program
{
/// <summary>
/// Contains the state for a client connection
/// </summary>
public class ClientState
{
public const int ReceiveBufferSize = 8192;
// The buffer to receive in
internal byte[] receiveBuffer = new byte[ReceiveBufferSize];
// The TcpClient instance representing the remote end (connected client)
public TcpClient TcpClient { get; set; }
public byte[] GetReceiveBuffer()
{
return receiveBuffer;
}
}
// This method is invoked when data is received from a client
static void OnReceive(IAsyncResult asyncResult)
{
// The state parameter passed to the BeginRead method
// is provided here in the asyncResult.AsyncState property
ClientState clientState = asyncResult.AsyncState as ClientState;
int numberOfBytesReceived = clientState.TcpClient.GetStream().EndRead(asyncResult);
if (numberOfBytesReceived == 0)
{
// This means that the transmission is over
Console.WriteLine("Client disconnect: {0}", clientState.TcpClient.Client.RemoteEndPoint);
return;
}
// Now the receiveBuffer is filled with <numberOfBytesReceived> bytes received from the client.
// Do whatever is needed here.
Console.WriteLine("Received {0} bytes from {1}", numberOfBytesReceived, clientState.TcpClient.Client.RemoteEndPoint);
// We are also sending some information back:
StreamWriter streamWriter = new StreamWriter(clientState.TcpClient.GetStream());
streamWriter.WriteLine("The server has received {0} bytes from you! Keep up the good job!", numberOfBytesReceived);
streamWriter.Flush();
// Begin read again
clientState.TcpClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);
}
// This method is invoked when a new client connects
static void OnConnect(IAsyncResult asyncResult)
{
// The state parameter passed to the BeginAcceptTcpClient method
// is provided here in the asyncResult.AsyncState property
TcpListener tcpListener = asyncResult.AsyncState as TcpListener;
// Accept the TcpClient:
TcpClient newClient = tcpListener.EndAcceptTcpClient(asyncResult);
// Immediately begin accept a new tcp client.
// We do not want to cause any latency for new connection requests
tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);
// Create the client state to store information aboutn the client connection
ClientState clientState = new ClientState()
{
TcpClient = newClient
};
Console.WriteLine("A new client has connected. IP Address: {0}", newClient.Client.RemoteEndPoint);
// Start receiving data from the client
// Please note that we are passing the buffer (byte[]) of the client state
// We are also passing the clientState instance as the state parameter
// this state parameter is retrieved using asyncResult.AsyncState in the asynchronous callback (OnReceive)
newClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);
// Nothing else to do.
// The rest of the communication process will be handled by OnReceive()
}
static void Main()
{
// Start a tcp listener
TcpListener tcpListener = new TcpListener(IPAddress.Loopback, 9988);
tcpListener.Start();
// Begin accept a new tcp client, pass the listener as the state
// The state is retrieved using asyncResult.AsyncState in the asynchronous callback (OnConnect)
tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);
// That's it. We don't need anything else here, except wait and see.
Console.WriteLine("Server is listening on port 9988. Press enter to stop.");
Console.ReadLine();
}
}
I'm implementing an application in .Net. I have to create a connection by SSH which is works, but the HL7 data receiving fails. The destination is a raspberry pi. So when I'm debugging the ssh client is connected, the port is forwarded, the tcp client also connected, but there is no answer for my queries. Plese suggest me some examples!
In this project I have already implemented it on Android - it works fine.
So in .Net I tried the NHapiTools library and I also tried the direct TcpClient way too. localPort = remotePort. I used localIP = "localhost"
static void Main(string[] args)
{
try
{
PrivateKeyFile file = new PrivateKeyFile(#"./key/private.key");
using (var client = new SshClient(remoteIP, sshPort, username, file))
{
client.Connect();
var ci = client.ConnectionInfo;
var port = new ForwardedPortLocal(localIP, localPort, client.ConnectionInfo.Host, remotePort);
client.AddForwardedPort(port);
port.Start();
var req = "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5";
////TCP
var tcpClient = new TcpClient();
tcpClient.Connect(localIP, (int)localPort);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
using (var stream = tcpClient.GetStream())
{
stream.Write(data, 0, data.Length);
using (var buffer = new MemoryStream())
{
byte[] chunk = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(chunk, 0, chunk.Length)) > 0)
{
buffer.Write(chunk, 0, bytesRead);
}
data = buffer.ToArray();
}
}
//I used this also with same result -> no respond
//SimpleMLLP
/*
var connection = new SimpleMLLPClient(localIP, localPort,
Encoding.UTF8);
var response = connection.SendHL7Message(req);
*/
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
}
So I experinced that the buffer size is 0 in TCP (due to time out). In the SimpleMLLP test SendHK7Message method never returns
You are not implementing MLLP (also called LLP) protocol while sending message.
Description HEX ASCII Symbol
Message starting character 0B 11 <VT>
Message ending characters 1C,0D 28,13 <FS>,<CR>
This way, when you send a message to Listener (TCP/MLLP server), it looks for Start Block in your incoming data. It never finds it. It just discards your entire message considering garbage. Hence, you get nothing back from Listener.
With MLLP implemented, your message (the stuff you are writing on socket) should look something like below:
<VT>MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5<FS><CR>
Note the <VT>, <CR> and <FS> are place holders in above message.
You may refer to this article for detailed information (Read step 4 and onward):
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace SimpleMllpHl7ClientAdvanced
{
public class Program
{
private static char END_OF_BLOCK = '\u001c';
private static char START_OF_BLOCK = '\u000b';
private static char CARRIAGE_RETURN = (char)13;
static void Main(string[] args)
{
TcpClient ourTcpClient = null;
NetworkStream networkStream = null;
var testHl7MessageToTransmit = new StringBuilder();
//a HL7 test message that is enveloped with MLLP as described in my article
testHl7MessageToTransmit.Append(START_OF_BLOCK)
.Append("MSH|^~\\&|AcmeHIS|StJohn|CATH|StJohn|20061019172719||ORM^O01|MSGID12349876|P|2.3")
.Append(CARRIAGE_RETURN)
.Append("PID|||20301||Durden^Tyler^^^Mr.||19700312|M|||88 Punchward Dr.^^Los Angeles^CA^11221^USA|||||||")
.Append(CARRIAGE_RETURN)
.Append("PV1||O|OP^^||||4652^Paulson^Robert|||OP|||||||||9|||||||||||||||||||||||||20061019172717|20061019172718")
.Append(CARRIAGE_RETURN)
.Append("ORC|NW|20061019172719")
.Append(CARRIAGE_RETURN)
.Append("OBR|1|20061019172719||76770^Ultrasound: retroperitoneal^C4|||12349876")
.Append(CARRIAGE_RETURN)
.Append(END_OF_BLOCK)
.Append(CARRIAGE_RETURN);
try
{
//initiate a TCP client connection to local loopback address at port 1080
ourTcpClient = new TcpClient();
ourTcpClient.Connect(new IPEndPoint(IPAddress.Loopback, 1080));
Console.WriteLine("Connected to server....");
//get the IO stream on this connection to write to
networkStream = ourTcpClient.GetStream();
//use UTF-8 and either 8-bit encoding due to MLLP-related recommendations
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
//send a message through this connection using the IO stream
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent data to server successfully....");
var receiveMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
// Our server for this example has been designed to echo back the message
// keep reading from this stream until the message is echoed back
while (bytesReceivedFromServer > 0)
{
if (networkStream.CanRead)
{
bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer == 0)
{
break;
}
}
}
var receivedMessage = Encoding.UTF8.GetString(receiveMessageByteBuffer);
Console.WriteLine("Received message from server: {0}", receivedMessage);
}
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
catch (Exception ex)
{
//display any exceptions that occur to console
Console.WriteLine(ex.Message);
}
finally
{
//close the IO strem and the TCP connection
networkStream?.Close();
ourTcpClient?.Close();
}
}
}
}
You should modify your following line of code as below:
var req = START_OF_BLOCK + "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5" + END_OF_BLOCK + CARRIAGE_RETURN;
For more open source code, you may refer to this github project.
After days of struggling I have solved the problem. The main error was with the port forwarding.
I would reccomend to use SSH.Net by Renci (There was algorithm error with Tamir ssh).
After ssh connection created I used this to port forward:
var port = new ForwardedPortLocal(localIP, localPort, "localhost", remotePort);
Check your localIP with ipconfig /all in cmd. Or use 127.0.0.1 as a loopback IP.
SimpleMLLPClient did not worked for me so I used the direct tcp client query way. Like this:
TcpClient ourTcpClient = new TcpClient();
ourTcpClient.Connect(localIP, (int)localPort);
NetworkStream networkStream = ourTcpClient.GetStream();
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent to server successfully....");
byte[] receiveMessageByteBuffer = new byte[ourTcpClient.ReceiveBufferSize];
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer > 0 && networkStream.CanRead)
{
receivedMessage.Append(Encoding.UTF8.GetString(receiveMessageByteBuffer));
}
var message = receivedMessage.Replace("\0", string.Empty);
Console.WriteLine("Received message from server: {0}", message);
}
So it gave me instant answer with 0 bytes (not due timeout). And here comes Amit Joshi help. I used a query what he suggested with START_OF_BLOCK, CARRIAGE_RETURN and END_OF_BLOCK and finally started to work. Thank you Amit Joshi!
Additional info:
In Android (java/Kotlin) jsch session setPortForwardingL works fine with three params:
val session = jsch.getSession("user", sshIP, sshPort)
session.setPassword("")
jsch.addIdentity(privatekey.getAbsolutePath())
// Avoid asking for key confirmation
val prop = Properties()
prop.setProperty("StrictHostKeyChecking", "no")
session.setConfig(prop)
session.connect(5000)
session.setPortForwardingL(localForwardPort, "localhost", remotePort)
val useTls = false
val context = DefaultHapiContext()
connection = context.newClient("localhost", localForwardPort, useTls)
I've created an universal app that connects to a WCF webservice at intranet, an it's working just fine, since the address of the service's host is known.
The system's architecture allows to be more than one webservice running, in different hosts, for performance and security (redundancy) reasons. So I'm trying to make my app discover every service, with the given contract, that is been running on the same LAN, but I can't manage to do that.
I'm trying the same approach used at a very similar win32 app:
var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var findCriteria = new FindCriteria(typeof(INewProdColetorWCFService));
findCriteria.Duration = TimeSpan.FromSeconds(5);
var findResponse = await discoveryClient.FindTaskAsync(findCriteria);
Visual Studio "automatically" adds the needed reference (System.ServiceModel.Discovery) for me as seen here
At design time it seems to be ok, but when i try to compile, that error appear:
Cannot find type System.ServiceModel.Configuration.ServiceModelConfigurationElementCollection`1 in module System.ServiceModel.dll.
Have any of you did that in UWP? Can you help me?
Thanks in advance, iuri.
ps: I've posted this question in MSDN too
I came across this thread whilst doing some research myself. After reading up on https://en.wikipedia.org/wiki/WS-Discovery and using Wireshark to decipher some of the specifics, I've got a basic proof of concept that supports Microsoft's WS-Discovery specification, meaning no changes are necessary on the server end.
I've jumped off the project for now, but hopefully someone might get some use from it:
public class WSDiscoveryResponse
{
private readonly string
_content,
_remotePort;
private readonly HostName
_remoteAddress;
public WSDiscoveryResponse(string content, HostName remoteAddress, string remotePort)
{
this._content = content;
this._remoteAddress = remoteAddress;
this._remotePort = remotePort;
}
}
public class WSDiscoveryClient
{
private const string
SRC_PORT = "0",//represents 'use any port available'
DEST_IP_WSDISCOVERY = "239.255.255.250", //broadcast (ish)
DEST_PORT_WSDISCOVERY = "3702";
private TimeSpan _timeout = TimeSpan.FromSeconds(5);
private List<WSDiscoveryResponse> _wsresponses = null;
/// <summary>
/// Get available Webservices
/// </summary>
public async Task<List<WSDiscoveryResponse>> GetAvailableWSEndpoints()
{
_wsresponses = new List<WSDiscoveryResponse>();
using (var socket = new DatagramSocket())
{
try
{
socket.MessageReceived += SocketOnMessageReceived;
//listen for responses to future message
await socket.BindServiceNameAsync(SRC_PORT);
//broadcast interrogation
await SendDiscoveryMessage(socket);
//wait for broadcast responses
await Task.Delay(_timeout).ConfigureAwait(false);
}
catch (Exception ex)
{
SocketErrorStatus webErrorStatus = SocketError.GetStatus(ex.GetBaseException().HResult);
}
}
return _wsresponses;
}
private string BuildDiscoveryMessage()
{
const string outgoingMessageFormat = #"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://www.w3.org/2005/08/addressing""><s:Header><a:Action s:mustUnderstand=""1"">http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Probe</a:Action><a:MessageID>urn:uuid:{0}</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=""1"">urn:docs-oasis-open-org:ws-dd:ns:discovery:2009:01</a:To></s:Header><s:Body><Probe xmlns=""http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01""><d:Types xmlns:d=""http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"" xmlns:dp0=""http://tempuri.org/"">dp0:IDiscoveryService</d:Types><Duration xmlns=""http://schemas.microsoft.com/ws/2008/06/discovery"">PT5S</Duration></Probe></s:Body></s:Envelope>";
string outgoingMessage = string.Format(outgoingMessageFormat, Guid.NewGuid().ToString());
return outgoingMessage;
}
private async Task SendDiscoveryMessage(DatagramSocket socket)
{
using (var stream = await socket.GetOutputStreamAsync(new HostName(DEST_IP_WSDISCOVERY), DEST_PORT_WSDISCOVERY))
{
string message = BuildDiscoveryMessage();
var data = Encoding.UTF8.GetBytes(message);
using (var writer = new DataWriter(stream))
{
writer.WriteBytes(data);
await writer.StoreAsync();
}
}
}
private void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
var dr = args.GetDataReader();
string message = dr.ReadString(dr.UnconsumedBufferLength);
_wsresponses.Add(new WSDiscoveryResponse(message, args.RemoteAddress, args.RemotePort));
}
}
UWP doesn’t support the WS-Discovery API for now. Details please see https://msdn.microsoft.com/en-us/library/windows/apps/mt185502.aspx.
There is no System.ServiceModel.Discovery API support for UWP apps in the document. But you can use it in a win32 application.
If you need this feature you can submit your idea to UserVoice site: https://wpdev.uservoice.com/forums/110705-universal-windows-platform
I don't know if I should answer my own question, but I think it may be useful for anyone trying to do the same, so here it goes.
Since WS-Discovery API is not available in UWP, I had to do it in another way. Using socket was the best alternative I could find. So every WS will listen to a specific port, awaiting for some broadcasted message searching for WS running in the LAN.
The WS implementation is win32, and this is the code needed:
private byte[] dataStream = new byte[1024];
private Socket serverSocket;
private void InitializeSocketServer(string id)
{
// Sets the server ID
this._id = id;
// Initialise the socket
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Initialise the IPEndPoint for the server and listen on port 30000
IPEndPoint server = new IPEndPoint(IPAddress.Any, 30000);
// Associate the socket with this IP address and port
serverSocket.Bind(server);
// Initialise the IPEndPoint for the clients
IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
// Initialise the EndPoint for the clients
EndPoint epSender = (EndPoint)clients;
// Start listening for incoming data
serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveData), epSender);
}
private void ReceiveData(IAsyncResult asyncResult)
{
// Initialise the IPEndPoint for the clients
IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
// Initialise the EndPoint for the clients
EndPoint epSender = (EndPoint)clients;
// Receive all data. Sets epSender to the address of the caller
serverSocket.EndReceiveFrom(asyncResult, ref epSender);
// Get the message received
string message = Encoding.UTF8.GetString(dataStream);
// Check if it is a search ws message
if (message.StartsWith("SEARCHWS", StringComparison.CurrentCultureIgnoreCase))
{
// Create a response messagem indicating the server ID and it's URL
byte[] data = Encoding.UTF8.GetBytes($"WSRESPONSE;{this._id};http://{GetIPAddress()}:5055/wsserver");
// Send the response message to the client who was searching
serverSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, epSender, new AsyncCallback(this.SendData), epSender);
}
// Listen for more connections again...
serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(this.ReceiveData), epSender);
}
private void SendData(IAsyncResult asyncResult)
{
serverSocket.EndSend(asyncResult);
}
The client implementation is UWP. I've created the following class to do the search:
public class WSDiscoveryClient
{
public class WSEndpoint
{
public string ID;
public string URL;
}
private List<WSEndpoint> _endPoints;
private int port = 30000;
private int timeOut = 5; // seconds
/// <summary>
/// Get available Webservices
/// </summary>
public async Task<List<WSEndpoint>> GetAvailableWSEndpoints()
{
_endPoints = new List<WSEndpoint>();
using (var socket = new DatagramSocket())
{
// Set the callback for servers' responses
socket.MessageReceived += SocketOnMessageReceived;
// Start listening for servers' responses
await socket.BindServiceNameAsync(port.ToString());
// Send a search message
await SendMessage(socket);
// Waits the timeout in order to receive all the servers' responses
await Task.Delay(TimeSpan.FromSeconds(timeOut));
}
return _endPoints;
}
/// <summary>
/// Sends a broadcast message searching for available Webservices
/// </summary>
private async Task SendMessage(DatagramSocket socket)
{
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), port.ToString()))
{
using (var writer = new DataWriter(stream))
{
var data = Encoding.UTF8.GetBytes("SEARCHWS");
writer.WriteBytes(data);
await writer.StoreAsync();
}
}
}
private async void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
// Creates a reader for the incoming message
var resultStream = args.GetDataStream().AsStreamForRead(1024);
using (var reader = new StreamReader(resultStream))
{
// Get the message received
string message = await reader.ReadToEndAsync();
// Cheks if the message is a response from a server
if (message.StartsWith("WSRESPONSE", StringComparison.CurrentCultureIgnoreCase))
{
// Spected format: WSRESPONSE;<ID>;<HTTP ADDRESS>
var splitedMessage = message.Split(';');
if (splitedMessage.Length == 3)
{
var id = splitedMessage[1];
var url = splitedMessage[2];
_endPoints.Add(new WSEndpoint() { ID = id, URL = url });
}
}
}
}
}
Feel free to comment if you see something wrong, and please tell me if it helps you someway.
I have an application that runs and continuously in the system tray icon. It makes sure it is the only running instance of my application by running some code that uses a #Global mutex.
Due to 3rd party vendor limitations the only (sane) way I can integrate with one part of a vendor app is by specifying in a config file that an arbitrary command line executable is called. The parameters I care about are automatically added to the executable I specify.
So I think most people who integrated with this run a seperate program which connects to a server socket on the main program and sends a packet letting the main program know the command line has been triggered.
I'm wondering if there is a way I could do this pseudo-code..
static void Main(string[] args)
{
if (isrunningcommandLine)
{
ConnectToAlreadyRunningProcessAndPassInfo(args); // find already running process by its process name and somehow pass the command line params
KillProgram();
}
else
{
CheckGlobalMutexAndStart();
}
}
Essentially if I am running via command line connect to the already running global process and pass it some data.
I control the code that will be the client as well as the server, they are in fact the same project.
You can use a global named mutex to detect if your app is already running.
If no run a server that will receive and manage the commands.
If yes run a client and just send the commands to the server.
Then use a TCP connection on localhost to exchange messages.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Test42
{
internal static class Program
{
private static int PORT = 1337;
private static void Main()
{
bool ok;
// Try to build a mutex.
var mutex = new Mutex(true, #"Test42", out ok);
// If build is ok, we run as a server.
// Otherwise, the server is already running, so we run as a client.
if (ok)
{
var server = new MyServer(PORT);
server.Start();
}
else
{
var r = new Random();
var message = "Ho Hey : " + r.Next(50);
var client = new MyClient();
client.Send(PORT, message);
}
}
}
internal class MyClient
{
/// <summary>
/// Send a message to localhost.
/// </summary>
/// <param name="port">The port to connect.</param>
/// <param name="message">The message to send.</param>
public void Send(int port, string message)
{
var client = new TcpClient();
var serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port);
client.Connect(serverEndPoint);
using (var stream = client.GetStream())
{
var messageBuffer = Encoding.Unicode.GetBytes(message);
var lengthBuffer = BitConverter.GetBytes(messageBuffer.Length);
// Send message length.
stream.Write(lengthBuffer, 0, lengthBuffer.Length);
// Send message.
stream.Write(messageBuffer, 0, messageBuffer.Length);
stream.Flush();
}
client.Close();
}
}
internal class MyServer
{
private readonly int _port;
public MyServer(int port)
{
_port = port;
}
public void Start()
{
Console.WriteLine("wait for messages");
var thread = new Thread(ThreadStart);
thread.Start();
}
private void ThreadStart()
{
var listener = new TcpListener(IPAddress.Any, _port);
listener.Start();
while (true)
{
var client = listener.AcceptTcpClient();
var clientThread = new Thread(ClientThreadStart);
clientThread.Start(client);
}
}
private void ClientThreadStart(object obj)
{
var client = obj as TcpClient;
if (client == null) return;
using (var stream = client.GetStream())
{
const int lengthLength = sizeof(int) / sizeof(byte);
// Read the message length.
var lengthBuffer = new byte[lengthLength];
stream.ReadAsync(lengthBuffer, 0, lengthLength).Wait();
var messageLength = BitConverter.ToInt32(lengthBuffer, 0);
// Read the message.
var messageBuffer = new byte[messageLength];
stream.ReadAsync(messageBuffer, 0, messageLength).Wait();
var message = Encoding.Unicode.GetString(messageBuffer);
Console.WriteLine("Client says: " + message);
}
client.Close();
}
}
}
My first question here. I am new to this kind of programming, and i've only programmed .NET web sites and forms.
Now, the company I work at, asks me to make an ActiveX component, that listens to UDP messages, and turns them into events.
The UDP msgs are send from Avaya system, so i was told that to test my ActiveX, at first I need to create an app, that only sends UDP (only one button that sends pre-defined UDP string). And then create listener socket, ordinary C# app, that will get those transmitted UDP string from the tests app. Both apps will work on the same machine.
Later, when i get this working, i need to make the listener an ActiveX component, but first things first.
I need to know if there are any good tutorials about this, and any idea on how to start? I am sorry for my ignorance, but i am really new on this and i don't really have any time to learn this since it has to be done in 2 weeks.
Thanks in advance.
edit: I managed to create 2 simple console applications, and was sending UDP messages between them successfully. The sender will be only for testing, and now I need to re-make my receiver to get the UDP message and 'translate' it to events. And lastly, to make it an ActiveX control...
Simple server and client:
public struct Received
{
public IPEndPoint Sender;
public string Message;
}
abstract class UdpBase
{
protected UdpClient Client;
protected UdpBase()
{
Client = new UdpClient();
}
public async Task<Received> Receive()
{
var result = await Client.ReceiveAsync();
return new Received()
{
Message = Encoding.ASCII.GetString(result.Buffer, 0, result.Buffer.Length),
Sender = result.RemoteEndPoint
};
}
}
//Server
class UdpListener : UdpBase
{
private IPEndPoint _listenOn;
public UdpListener() : this(new IPEndPoint(IPAddress.Any,32123))
{
}
public UdpListener(IPEndPoint endpoint)
{
_listenOn = endpoint;
Client = new UdpClient(_listenOn);
}
public void Reply(string message,IPEndPoint endpoint)
{
var datagram = Encoding.ASCII.GetBytes(message);
Client.Send(datagram, datagram.Length,endpoint);
}
}
//Client
class UdpUser : UdpBase
{
private UdpUser(){}
public static UdpUser ConnectTo(string hostname, int port)
{
var connection = new UdpUser();
connection.Client.Connect(hostname, port);
return connection;
}
public void Send(string message)
{
var datagram = Encoding.ASCII.GetBytes(message);
Client.Send(datagram, datagram.Length);
}
}
class Program
{
static void Main(string[] args)
{
//create a new server
var server = new UdpListener();
//start listening for messages and copy the messages back to the client
Task.Factory.StartNew(async () => {
while (true)
{
var received = await server.Receive();
server.Reply("copy " + received.Message, received.Sender);
if (received.Message == "quit")
break;
}
});
//create a new client
var client = UdpUser.ConnectTo("127.0.0.1", 32123);
//wait for reply messages from server and send them to console
Task.Factory.StartNew(async () => {
while (true)
{
try
{
var received = await client.Receive();
Console.WriteLine(received.Message);
if (received.Message.Contains("quit"))
break;
}
catch (Exception ex)
{
Debug.Write(ex);
}
}
});
//type ahead :-)
string read;
do
{
read = Console.ReadLine();
client.Send(read);
} while (read != "quit");
}
}
Simple server and client:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
class Program
{
static void Main(string[] args)
{
// Create UDP client
int receiverPort = 20000;
UdpClient receiver = new UdpClient(receiverPort);
// Display some information
Console.WriteLine("Starting Upd receiving on port: " + receiverPort);
Console.WriteLine("Press any key to quit.");
Console.WriteLine("-------------------------------\n");
// Start async receiving
receiver.BeginReceive(DataReceived, receiver);
// Send some test messages
using (UdpClient sender1 = new UdpClient(19999))
sender1.Send(Encoding.ASCII.GetBytes("Hi!"), 3, "localhost", receiverPort);
using (UdpClient sender2 = new UdpClient(20001))
sender2.Send(Encoding.ASCII.GetBytes("Hi!"), 3, "localhost", receiverPort);
// Wait for any key to terminate application
Console.ReadKey();
}
private static void DataReceived(IAsyncResult ar)
{
UdpClient c = (UdpClient)ar.AsyncState;
IPEndPoint receivedIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] receivedBytes = c.EndReceive(ar, ref receivedIpEndPoint);
// Convert data to ASCII and print in console
string receivedText = ASCIIEncoding.ASCII.GetString(receivedBytes);
Console.Write(receivedIpEndPoint + ": " + receivedText + Environment.NewLine);
// Restart listening for udp data packages
c.BeginReceive(DataReceived, ar.AsyncState);
}
}
Server
public void serverThread()
{
UdpClient udpClient = new UdpClient(8080);
while(true)
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
string returnData = Encoding.ASCII.GetString(receiveBytes);
lbConnections.Items.Add(RemoteIpEndPoint.Address.ToString()
+ ":" + returnData.ToString());
}
}
And initialize the thread
private void Form1_Load(object sender, System.EventArgs e)
{
Thread thdUDPServer = new Thread(new ThreadStart(serverThread));
thdUDPServer.Start();
}
Client
private void button1_Click(object sender, System.EventArgs e)
{
UdpClient udpClient = new UdpClient();
udpClient.Connect(txtbHost.Text, 8080);
Byte[] senddata = Encoding.ASCII.GetBytes("Hello World");
udpClient.Send(senddata, senddata.Length);
}
Insert it to button command.
Source: http://technotif.com/creating-simple-udp-server-client-transfer-data-using-c-vb-net/