Windows 10 Universal App - Socket connection recurrent readings - c#

I am following the Socket Example about StreamSockets. I can read from the server as a client. The example is https://social.msdn.microsoft.com/Forums/es-ES/79eda064-473d-4398-8fe3-72369e686c3c/comunicacion-tcpcip-con-streamsocket-y-datawriterdatareader?forum=esdevwindows
I'm building a Windows 10 Universal Application and I need continuous readings from a socket connection. I'm getting the first reading but then only empty strings.
Thanks!
Image capture response
Private StreamSocket socketForServer;
Private DataWriter cfaStreamWriter;
Private DataReader cfaStreamReader;
// Read socket data
Private Async void ReadData()
{
If (socketForServer == null) Then Return;
uint s = await cfaStreamReader.LoadAsync(1024);
String Data = cfaStreamReader.ReadString(s);
PrintData(Data);
var ignore = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () >=
ReadData());
}
// Connect to the bid controller through TCP Socket.
Public Async Task<bool> Connect()
{
socketForServer = New StreamSocket();
var hostname = New HostName(deviceSPACS_IP);
await socketForServer.ConnectAsync(hostname, deviceSPACS_PORT.ToString());
// send login
SendRawMessage("WELCOME TO SERVER");
cfaStreamReader = New DataReader(socketForServer.InputStream)
{
InputStreamOptions = InputStreamOptions.Partial
};
ReadData();
Return True;
}
// Disconnect from bid Controller
Public bool Disconnect()
{
If (socketForServer == null) Then Return False;
SendRawMessage("GOOD BYE AL SERVER");
cfaStreamReader.Dispose();
cfaStreamReader = null;
socketForServer.Dispose();
socketForServer = null;
Return True;
}
Private Async void SendRawMessage(String message)
{
cfaStreamWriter = New DataWriter(socketForServer.OutputStream); cfaStreamWriter.WriteString(message + "\r\n");
await cfaStreamWriter.StoreAsync();
await cfaStreamWriter.FlushAsync();
cfaStreamWriter.DetachStream();
cfaStreamWriter.Dispose();
cfaStreamWriter = null;
}

Related

Backgroundtask is activated by SocketActivityTrigger, but the socket in the trigger returns null

I want to be able to send and receive data over a socket using bluetooth when my app is suspended. I register my BackgroundReader once a connection is established in the foreground app.
Code run once a connection is established:
private void OnConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
BackgroundTaskRegistration receiveTask = RegisterBackgroundTask("X.ReceiverTask", "ReceiverTask", new SocketActivityTrigger(), null);
BackgroundTaskRegistration sendTask = RegisterBackgroundTask("X.Z", "SenderTask", new ApplicationTrigger(), null);
var socket = args.Socket;
socket.EnableTransferOwnership(receiveTask.TaskId, SocketActivityConnectedStandbyAction.DoNotWake);
socket.TransferOwnership("RfcommSocket");
}
Method to register a Background task:
private static BackgroundTaskRegistration RegisterBackgroundTask(string taskEntryPoint, string name, IBackgroundTrigger trigger, IBackgroundCondition condition)
{
foreach(var cur in BackgroundTaskRegistration.AllTasks)
{
if(cur.Value.Name == name)
{
//task is already registered
return (BackgroundTaskRegistration)(cur.Value);
}
}
var builder = new BackgroundTaskBuilder();
builder.Name = name;
if(taskEntryPoint != null && taskEntryPoint != String.Empty)
{
builder.TaskEntryPoint = taskEntryPoint;
}
builder.SetTrigger(trigger);
if(condition != null)
{
builder.AddCondition(condition);
}
BackgroundTaskRegistration task = builder.Register();
return task;
}
When I try to send data the Receiver background task activates as expected. Only when I try to read the stream socket from its properties, it returns null and throws an Exception when I try to access it:
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskDeferral = taskInstance.GetDeferral();
taskInstance.Canceled += OnTaskCanceled;
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
var socketInformation = details.SocketInformation;
switch (details.Reason)
{
...
case SocketActivityTriggerReason.SocketActivity:
Debug.WriteLine(socketInformation.SocketKind.ToString());
var socket = socketInformation.StreamSocket; //This returns null
DataReader reader = new DataReader(socket.InputStream);
reader.InputStreamOptions = InputStreamOptions.Partial;
await reader.LoadAsync(250);
var dataString = reader.ReadString(reader.UnconsumedBufferLength);
Debug.WriteLine(dataString);
socket.TransferOwnership(socketInformation.Id); /* Important! */
break;
...
}
taskDeferral.Complete();
}
The backgroundTaskHost.exe exits and, supposedly, takes the socket with it.
Thanks in advance!
Backgroundtask is activated by SocketActivityTrigger, but the socket in the trigger returns null
I checked your code, The problem is you used SocketActivityTrigger in Bluetooth RF communication. In UWP we often use RfcommConnectionTrigger to detect Bluetooth message notification. The RfcommConnectionTrigger has more features that support Bluetooth RF communication. And I have test with official code sample and it could get the socket instance correctly and could load the message with reader.

What is the best place to declare the connection of my socket and my spot in the background?

I develop an HMI under the UWP, it needs a connection to a server and a background task to monitor whether messages are received.
At the launch of the HMI, an extended splash screen is appearing with a progress ring. To unblock the startup, you have to receive "launch_ok " from the server then we have access to the main page which allows to manage calls.
Currently I declare everything in my file: ExtendedSplash.xaml.cs
I declare my new socket with these settings, I run it and then I run my background activity.
I have also some errors : "Exception levée : 'System.NullReferenceException'"
ExtendedSplash.xaml.cs (Extract):
namespace PhoneCenter
{
partial class ExtendedSplash : Page
{
// SOCKET CONFIGURATION
private const string socketId = "SampleSocket";
private StreamSocket socket = null;
private IBackgroundTaskRegistration task = null;
private const string port = "40404";
private const string adress = "172.16.161.80";
}
public ExtendedSplash(SplashScreen splashscreen, bool loadState)
{
InitializeComponent();
Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
splash = splashscreen;
Debug.WriteLine("Création de la tâche d'arrière plan");
StartBackgroundTask();
Debug.WriteLine("Connexion Socket ...");
StartConnexionServeurLTO();
if (splash != null)
{
splash.Dismissed += new TypedEventHandler<SplashScreen, object>(DismissedEventHandler);
splashImageRect = splash.ImageLocation;
PositionImage();
PositionRing();
PositionTextBlock();
}
rootFrame = new Frame();
RestoreState(loadState);
}
// CONNEXION SOCKET
private async void StartConnexionServeurLTO()
{
ApplicationData.Current.LocalSettings.Values["hostname"] = adress;
ApplicationData.Current.LocalSettings.Values["port"] = port;
try
{
SocketActivityInformation socketInformation;
if (!SocketActivityInformation.AllSockets.TryGetValue(socketId, out socketInformation))
{
Debug.WriteLine("Boucle");
socket = new StreamSocket();
socket.EnableTransferOwnership(task.TaskId, SocketActivityConnectedStandbyAction.Wake);
var targetServer = new HostName(adress);
await socket.ConnectAsync(targetServer, port);
DataReader reader = new DataReader(socket.InputStream);
reader.InputStreamOptions = InputStreamOptions.Partial;
var read = reader.LoadAsync(250);
read.Completed += (info, status) =>
{
};
await socket.CancelIOAsync();
socket.TransferOwnership(socketId);
socket = null;
}
}
catch
{
Debug.WriteLine("Echec dans la connexion au serveur");
}
}
// LANCEMENT TÂCHE EN ARRIERE PLAN
private void StartBackgroundTask()
{
try
{
foreach (var current in BackgroundTaskRegistration.AllTasks)
{
if (current.Value.Name == "PhonieMarthaBackground")
{
task = current.Value;
break;
}
}
if (task == null)
{
var socketTaskBuilder = new BackgroundTaskBuilder();
socketTaskBuilder.Name = "PhonieMarthaBackground";
socketTaskBuilder.TaskEntryPoint = "PhonieMarthaBackground.SocketActivityTask";
var trigger = new SocketActivityTrigger();
socketTaskBuilder.SetTrigger(trigger);
task = socketTaskBuilder.Register();
}
SocketActivityInformation socketInformation;
if (SocketActivityInformation.AllSockets.TryGetValue(socketId, out socketInformation))
{
socket = socketInformation.StreamSocket;
socket.TransferOwnership(socketId);
socket = null;
}
Debug.WriteLine("Tâche d'arrière plan démarrée");
}
catch (Exception exception)
{
Debug.WriteLine(exception.Message);
}
}
}
Wouldn't it be simpler to perform these actions in the App.xaml.cs file when launching OnLaunched? Could we call a function at the end of OnLaunched?
The issue you are facing is that in the if you are checking whether the value was not found:
if (!SocketActivityInformation.AllSockets.TryGetValue(socketId, out socketInformation))
{
// Your code
}
If this value is false, then a value for socketId was not found, therefore socketInformation (which was not initialized) will get the default value, which is null. This is the reason of the problem. You should change your if to enter to the code block if the value was found instead:
if (!SocketActivityInformation.AllSockets.TryGetValue(socketId, out socketInformation))
{
// Your code
}

TcpClient exceptions when calling EndReceive and BeginReceive

I'm trying to implement wrapper class which will simply connect to TCP server and wait for data. Once data submitted from server - I will receive this data and pass it onto subscribers of my class.
All this works. Now I want to add external functionality to "reset" this class on a timer (force reconnect every so often) to keep connection alive. My idea is that Init method can be called as many times as needed to get socket reset. However, I do get various exceptions with this.
Class code:
namespace Ditat.GateControl.Service.InputListener
{
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class BaseTCPSocketListener : IInputListener
{
#region Events/Properties
public event EventHandler<Exception> OnError;
public event EventHandler<string> OnDataReceived;
private string host;
private int port;
private int delayToClearBufferSeconds = 5;
private TcpClient client;
private readonly byte[] buffer = new byte[1024];
/// <summary>
/// Will accumulate data as it's received
/// </summary>
private string DataBuffer { get; set; }
/// <summary>
/// Store time of last data receipt. Need this in order to purge data after delay
/// </summary>
private DateTime LastDataReceivedOn { get; set; }
#endregion
public BaseTCPSocketListener()
{
// Preset all entries
this.LastDataReceivedOn = DateTime.UtcNow;
this.DataBuffer = string.Empty;
}
public void Init(string config)
{
// Parse info
var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
this.host = bits[0];
var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
this.port = int.Parse(bits[1]);
this.delayToClearBufferSeconds = int.Parse(bits[2]);
// Close open client
if (this.client?.Client != null)
{
this.client.Client.Disconnect(true);
this.client = null;
}
// Connect to client
this.client = new TcpClient();
if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");
this.EstablishReceiver();
}
protected void DataReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;
try
{
bytesCount = this.client.Client.EndReceive(result);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}
// No data received, establish receiver and return
if (bytesCount == 0)
{
this.EstablishReceiver();
return;
}
// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(this.buffer, 0, bytesCount);
// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);
this.DataBuffer = string.Empty;
this.EstablishReceiver();
}
private void EstablishReceiver()
{
try
{
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, this.DataReceived, this.buffer);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}
private void RaiseOnErrorToClient(Exception ex)
{
if (this.OnError == null) return;
foreach (Delegate d in this.OnError.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, ex);
}
else
{
syncer.BeginInvoke(d, new object[] { this, ex });
}
}
}
private void RaiseOnDataReceivedToClient(string data)
{
if (this.OnDataReceived == null) return;
foreach (Delegate d in this.OnDataReceived.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, data);
}
else
{
syncer.BeginInvoke(d, new object[] { this, data });
}
}
}
}
}
Client code (under button click on form)
private void ListenBaseButton_Click(object sender, EventArgs e)
{
if (this.bsl == null)
{
this.bsl = new BaseTCPSocketListener();
this.bsl.OnDataReceived += delegate (object o, string s)
{
this.DataTextBox.Text += $"Base: {DateTime.Now} - {s}" + Environment.NewLine;
};
this.bsl.OnError += delegate (object o, Exception x)
{
this.DataTextBox.Text += $"Base TCP receiver error: {DateTime.Now} - {x.Message}" + Environment.NewLine;
};
}
try
{
this.bsl.Init("192.168.33.70|10001|10");
this.DataTextBox.Text += "BEGIN RECEIVING BSL data --------------------------" + Environment.NewLine;
}
catch (Exception exception)
{
this.DataTextBox.Text += $"ERROR CONNECTING TO BSL ------------{exception.Message}" + Environment.NewLine;
}
}
Exceptions I get. First exception when button clicked 2nd time in from handler in DataReceived
The IAsyncResult object was not returned from the corresponding
asynchronous method on this class.
On following clicks I get exception from handler in EstablishReceiver
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
How do I properly ensure socket closed and re-opened?
The IAsyncResult object was not returned from the corresponding
asynchronous method on this class.
This is a well known problem that happens when data callback (DataReceived()) is called for previous socket. In this case you will call Socket.EndReceive() with incorrect instance of IAsyncResult which throws above exception.
Asynchronous Client Socket Example contains possible workaround for this problem: store socket on which BeginReceive() was called in state object which is then passed to DataReceived callback:
StateObject class
public class StateObject
{
public Socket Socket { get; set; }
public byte[] Buffer { get; } = new byte[1024];
public StateObject(Socket socket)
{
Socket = socket;
}
}
EstablishReceiver() method:
private void EstablishReceiver()
{
try
{
var state = new StateObject(client.Client);
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, this.DataReceived, state);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}
DataReceived() method:
protected void DataReceived(IAsyncResult result)
{
var state = (StateObject) result.AsyncState;
// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;
try
{
SocketError errorCode;
bytesCount = state.Socket.EndReceive(result, out errorCode);
if (errorCode != SocketError.Success)
{
bytesCount = 0;
}
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}
if (bytesCount > 0)
{
// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(state.Buffer, 0, bytesCount);
// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);
this.DataBuffer = string.Empty;
this.EstablishReceiver();
}
}
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
Above DataReceived() method also contains the fix for the second exception. Exception is caused by calling BeginReceive() (from EstablishReceiver()) on disconnected socket. You should not call BeginReceive() on a socket if previous read brought 0 bytes.
First of all, you're closing the socket being held by the TcpClient, but not disposing the client itself. Try the following:
// Close open client
this.client?.Close(); // Disposes and releases resources
this.client = null;
The issue is that DataReceived will be called when you close the client. You simply need to identify to the method that it should not do anything because you have deliberately ended the process. You could just add a bool:
private bool ignoreCallback;
public void Init(string config)
{
// Parse info
var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
this.host = bits[0];
var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
this.port = int.Parse(bits[1]);
this.delayToClearBufferSeconds = int.Parse(bits[2]);
// Close open client
if (this.client?.Client != null)
{
ignoreCallback = true;
this.client.Client.Disconnect(true);
this.client = null;
}
// Connect to client
this.client = new TcpClient();
if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");
this.EstablishReceiver();
}
protected void DataReceived(IAsyncResult result)
{
if (ignoreCallback)
{
ignoreCallback = false;
return;
}
...

Sending data over NetworkStream using multiple threads

I'm trying to build a command line chat room where the server is handling the connections and repeating input from one client back to all the other clients.
Currently the server is able to take in input from multiple clients, but can only send information back to those clients individually. I think my problem is that each connection is being handled on an individual thread. How would I allow for the threads to communicate with each other or be able to send data to each thread?
Server code:
namespace ConsoleApplication
{
class TcpHelper
{
private static object _lock = new object();
private static List<Task> _connections = new List<Task>();
private static TcpListener listener { get; set; }
private static bool accept { get; set; } = false;
private static Task StartListener()
{
return Task.Run(async () =>
{
IPAddress address = IPAddress.Parse("127.0.0.1");
int port = 5678;
listener = new TcpListener(address, port);
listener.Start();
Console.WriteLine($"Server started. Listening to TCP clients at 127.0.0.1:{port}");
while (true)
{
var tcpClient = await listener.AcceptTcpClientAsync();
Console.WriteLine("Client has connected");
var task = StartHandleConnectionAsync(tcpClient);
if (task.IsFaulted)
task.Wait();
}
});
}
// Register and handle the connection
private static async Task StartHandleConnectionAsync(TcpClient tcpClient)
{
// start the new connection task
var connectionTask = HandleConnectionAsync(tcpClient);
// add it to the list of pending task
lock (_lock)
_connections.Add(connectionTask);
// catch all errors of HandleConnectionAsync
try
{
await connectionTask;
}
catch (Exception ex)
{
// log the error
Console.WriteLine(ex.ToString());
}
finally
{
// remove pending task
lock (_lock)
_connections.Remove(connectionTask);
}
}
private static async Task HandleConnectionAsync(TcpClient client)
{
await Task.Yield();
{
using (var networkStream = client.GetStream())
{
if (client != null)
{
Console.WriteLine("Client connected. Waiting for data.");
StreamReader streamreader = new StreamReader(networkStream);
StreamWriter streamwriter = new StreamWriter(networkStream);
string clientmessage = "";
string servermessage = "";
while (clientmessage != null && clientmessage != "quit")
{
clientmessage = await streamreader.ReadLineAsync();
Console.WriteLine(clientmessage);
servermessage = clientmessage;
streamwriter.WriteLine(servermessage);
streamwriter.Flush();
}
Console.WriteLine("Closing connection.");
networkStream.Dispose();
}
}
}
}
public static void Main(string[] args)
{
// Start the server
Console.WriteLine("Hit Ctrl-C to close the chat server");
TcpHelper.StartListener().Wait();
}
}
}
Client Code:
namespace Client2
{
public class Program
{
private static void clientConnect()
{
TcpClient socketForServer = new TcpClient();
bool status = true;
string userName;
Console.Write("Input Username: ");
userName = Console.ReadLine();
try
{
IPAddress address = IPAddress.Parse("127.0.0.1");
socketForServer.ConnectAsync(address, 5678);
Console.WriteLine("Connected to Server");
}
catch
{
Console.WriteLine("Failed to Connect to server{0}:999", "localhost");
return;
}
NetworkStream networkStream = socketForServer.GetStream();
StreamReader streamreader = new StreamReader(networkStream);
StreamWriter streamwriter = new StreamWriter(networkStream);
try
{
string clientmessage = "";
string servermessage = "";
while (status)
{
Console.Write(userName + ": ");
clientmessage = Console.ReadLine();
if ((clientmessage == "quit") || (clientmessage == "QUIT"))
{
status = false;
streamwriter.WriteLine("quit");
streamwriter.WriteLine(userName + " has left the conversation");
streamwriter.Flush();
}
if ((clientmessage != "quit") && (clientmessage != "quit"))
{
streamwriter.WriteLine(userName + ": " + clientmessage);
streamwriter.Flush();
servermessage = streamreader.ReadLine();
Console.WriteLine("Server:" + servermessage);
}
}
}
catch
{
Console.WriteLine("Exception reading from the server");
}
streamreader.Dispose();
networkStream.Dispose();
streamwriter.Dispose();
}
public static void Main(string[] args)
{
clientConnect();
}
}
}
The main thing wrong in your code is that you make no attempt to send data received from one client to the other connected clients. You have the _connections list in your server, but the only thing stored in the list are the Task objects for the connections, and you don't even do anything with those.
Instead, you should maintain a list of the connections themselves, so that when you received a message from one client, you can then retransmit that message to the other clients.
At a minimum, this should be a List<TcpClient>, but because you are using StreamReader and StreamWriter, you'll want to initialize and store those objects in the list as well. In addition, you should include a client identifier. One obvious choice for this would be the name of the client (i.e. what the user enters as their name), but your example doesn't provide any mechanism in the chat protocol to transmit that identification as part of the connection initialization, so in my example (below) I just use a simple integer value.
There are some other irregularities in the code you posted, such as:
Starting a task in a brand new thread, just to execute a few statements that get you to the point of initiating an asynchronous operation. In my example, I simply omit the Task.Run() part of the code, as it's not needed.
Checking the connection-specific task when it's returned for IsFaulted. Since it's unlikely any I/O will actually have occurred by the time this Task object is returned, this logic has very little use. The call to Wait() will throw an exception, which will propagate to the main thread's Wait() call, terminating the server. But you don't terminate the server in the event of any other error, so it's not clear why you'd want to do that here.
There's a spurious call to Task.Yield(). I have no idea what you're trying to accomplish there, but whatever it is, that statement isn't useful. I simply removed it.
In your client code, you only attempt to receive data from the server when you've sent data. This is very wrong; you want clients to be responsive and receive data as soon as it's sent to them. In my version, I included a simple little anonymous method that is called immediately to start a separate message-receiving loop that will execute asynchronously and concurrently with the main user input loop.
Also in the client code, you were sending the "…has left…" message after the "quit" message that would cause the server to close the connection. This means that the server would never actually receive the "…has left…" message. I reversed the order of the messages so that "quit" is always the last thing the client ever sends.
My version looks like this:
Server:
class TcpHelper
{
class ClientData : IDisposable
{
private static int _nextId;
public int ID { get; private set; }
public TcpClient Client { get; private set; }
public TextReader Reader { get; private set; }
public TextWriter Writer { get; private set; }
public ClientData(TcpClient client)
{
ID = _nextId++;
Client = client;
NetworkStream stream = client.GetStream();
Reader = new StreamReader(stream);
Writer = new StreamWriter(stream);
}
public void Dispose()
{
Writer.Close();
Reader.Close();
Client.Close();
}
}
private static readonly object _lock = new object();
private static readonly List<ClientData> _connections = new List<ClientData>();
private static TcpListener listener { get; set; }
private static bool accept { get; set; }
public static async Task StartListener()
{
IPAddress address = IPAddress.Any;
int port = 5678;
listener = new TcpListener(address, port);
listener.Start();
Console.WriteLine("Server started. Listening to TCP clients on port {0}", port);
while (true)
{
var tcpClient = await listener.AcceptTcpClientAsync();
Console.WriteLine("Client has connected");
var task = StartHandleConnectionAsync(tcpClient);
if (task.IsFaulted)
task.Wait();
}
}
// Register and handle the connection
private static async Task StartHandleConnectionAsync(TcpClient tcpClient)
{
ClientData clientData = new ClientData(tcpClient);
lock (_lock) _connections.Add(clientData);
// catch all errors of HandleConnectionAsync
try
{
await HandleConnectionAsync(clientData);
}
catch (Exception ex)
{
// log the error
Console.WriteLine(ex.ToString());
}
finally
{
lock (_lock) _connections.Remove(clientData);
clientData.Dispose();
}
}
private static async Task HandleConnectionAsync(ClientData clientData)
{
Console.WriteLine("Client connected. Waiting for data.");
string clientmessage;
while ((clientmessage = await clientData.Reader.ReadLineAsync()) != null && clientmessage != "quit")
{
string message = "From " + clientData.ID + ": " + clientmessage;
Console.WriteLine(message);
lock (_lock)
{
// Locking the entire operation ensures that a) none of the client objects
// are disposed before we can write to them, and b) all of the chat messages
// are received in the same order by all clients.
foreach (ClientData recipient in _connections.Where(r => r.ID != clientData.ID))
{
recipient.Writer.WriteLine(message);
recipient.Writer.Flush();
}
}
}
Console.WriteLine("Closing connection.");
}
}
Client:
class Program
{
private const int _kport = 5678;
private static async Task clientConnect()
{
IPAddress address = IPAddress.Loopback;
TcpClient socketForServer = new TcpClient();
string userName;
Console.Write("Input Username: ");
userName = Console.ReadLine();
try
{
await socketForServer.ConnectAsync(address, _kport);
Console.WriteLine("Connected to Server");
}
catch (Exception e)
{
Console.WriteLine("Failed to Connect to server {0}:{1}", address, _kport);
return;
}
using (NetworkStream networkStream = socketForServer.GetStream())
{
var readTask = ((Func<Task>)(async () =>
{
using (StreamReader reader = new StreamReader(networkStream))
{
string receivedText;
while ((receivedText = await reader.ReadLineAsync()) != null)
{
Console.WriteLine("Server:" + receivedText);
}
}
}))();
using (StreamWriter streamwriter = new StreamWriter(networkStream))
{
try
{
while (true)
{
Console.Write(userName + ": ");
string clientmessage = Console.ReadLine();
if ((clientmessage == "quit") || (clientmessage == "QUIT"))
{
streamwriter.WriteLine(userName + " has left the conversation");
streamwriter.WriteLine("quit");
streamwriter.Flush();
break;
}
else
{
streamwriter.WriteLine(userName + ": " + clientmessage);
streamwriter.Flush();
}
}
await readTask;
}
catch (Exception e)
{
Console.WriteLine("Exception writing to server: " + e);
throw;
}
}
}
}
public static void Main(string[] args)
{
clientConnect().Wait();
}
}
There is still a lot you'll need to work on. You'll probably want to implement proper initialization of chat user names on the server side. At the very least, for real-world code you'd want to do more error checking, and make sure the client ID is generated reliably (if you only want positive ID values, you can't have more than 2^31-1 connections before it rolls back over to 0).
I also made some other minor changes that weren't strictly necessary, such as using the IPAddress.Any and IPAddress.Loopback values instead of parsing strings, and just generally simplifying and cleaning up the code here and there. Also, I'm not using a C# 6 compiler at the moment, so I changed the code where you were using C# 6 features so that it would compile using C# 5 instead.
To do a full-blown chat server, you still have your work cut out for you. But I hope that the above gets you back on the right track.

Read data from StreamSocketListener on RFComm bluetooth connected device C#

I'm developing an universal application Win8.1 / WP8.1
I'm able to discover and connect to the paired bluetooth devices (Stick readers - Rfid)
This is how I'm connecting
Variables
private IAsyncOperation<RfcommDeviceService> connectService;
private IAsyncAction connectAction;
private RfcommDeviceService rfcommService;
private RfcommServiceProvider rfcommProvider;
private StreamSocketListener listener;
private DataReader reader;
private DataWriter writer;
//Connection
public async Task ConnectToServiceAsync(string name)
{
DeviceInformation serviceInfo = null;
foreach (var device in devices)
{
if(device.Name == name)
{
serviceInfo = device;
break;
}
}
if (serviceInfo != null)
{
this.State = BluetoothConnectionState.Connecting;
try
{
// Initialize the target Bluetooth RFCOMM device service
connectService = RfcommDeviceService.FromIdAsync(serviceInfo.Id);
rfcommService = await connectService;
if (rfcommService != null)
{
rfcommProvider = await RfcommServiceProvider.CreateAsync(rfcommService.ServiceId);
// Create a socket and connect to the target
listener = new StreamSocketListener();
listener.ConnectionReceived += Listener_ConnectionReceived;
connectAction = listener.BindServiceNameAsync(rfcommService.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
this.State = BluetoothConnectionState.Connected;
}
else
OnExceptionOccuredEvent(this, new Exception("Unable to create service.\nMake sure that the 'bluetooth.rfcomm' capability is declared with a function of type 'name:serialPort' in Package.appxmanifest."));
}
catch (TaskCanceledException)
{
this.State = BluetoothConnectionState.Disconnected;
}
catch (Exception ex)
{
this.State = BluetoothConnectionState.Disconnected;
OnExceptionOccuredEvent(this, ex);
}
}
}
//Then wait for a connection over the listener
private async void Listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader inputreader = new DataReader(args.Socket.InputStream);
while (true)
{
try
{
inputreader.InputStreamOptions = InputStreamOptions.Partial;
// Read first byte (length of the subsequent message, 255 or less).
uint sizeFieldCount = await inputreader.LoadAsync(1);
if (sizeFieldCount != 1)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message.
uint messageLength = inputreader.ReadByte();
uint actualMessageLength = await inputreader.LoadAsync(messageLength);
if (messageLength != actualMessageLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message and process it.
string message = inputreader.ReadString(actualMessageLength);
OnMessageReceivedEvent(this, message);
}
catch (Exception ex)
{
if (inputreader != null)
OnExceptionOccuredEvent(this, ex);
}
}
}
The problem is that the Bluetooth Stick Reader never send a connection request, it just read the rfid device ID and sends it over the serial port.
So, I'm able to connect to the device but I don't know how to actively listen or read the incoming data.
Any help will be appreciated.
RFCOMM communication is Client/Server model. You need to define a server to broadcast the service and a client connected to the service.
As I saw from the code you post, it only includes the server side code which provides the service but there was no client connecting to service. Only when the client connected to the service, the ConnectionReceived event will be fired.
I have written a sample which host the RFCOMM service in a console application and consume the service in Windows Runtime App before (Code Sample).
The client code is as following:
rfcommServiceInfoCollection = await DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(RfcommServiceId.ObexObjectPush));
var count = rfcommServiceInfoCollection.Count;
Debug.WriteLine("Count of RFCOMM Service: " + count);
if(count > 0)
{
lock (this)
{
streamSocket = new StreamSocket();
}
var defaultSvcInfo = rfcommServiceInfoCollection.FirstOrDefault();
rfcommDeviceService = await RfcommDeviceService.FromIdAsync(defaultSvcInfo.Id);
if(rfcommDeviceService == null)
{
Debug.WriteLine("Rfcomm Device Service is NULL, ID = {0}", defaultSvcInfo.Id);
return;
}
Debug.WriteLine("ConnectionHostName: {0}, ConnectionServiceName: {1}", rfcommDeviceService.ConnectionHostName, rfcommDeviceService.ConnectionServiceName);
await streamSocket.ConnectAsync(rfcommDeviceService.ConnectionHostName, rfcommDeviceService.ConnectionServiceName);
By the way, do not forget to add the RFCOMM capabilities in your appxmanifest.
For example:
<m2:DeviceCapability Name="bluetooth.rfcomm">
<m2:Device Id="any">
<m2:Function Type="name:obexObjectPush" />
</m2:Device>
</m2:DeviceCapability
As Jeffrey said, this is a client/Server model, but in my case the client doesn't have any logic, and is not capable to request any connection.
Thank you for your samples, It helped a lot.
After trying several things I got some code working, and a "server" running and listening in the rfcomm service:
public async Task ConnectToServiceAsync(string name)
{
lock(this.interlock)
{
readBuffer = String.Empty;
}
DeviceInformation serviceInfo = null;
foreach (var device in devices)
{
if(device.Name == name)
{
serviceInfo = device;
break;
}
}
if (serviceInfo != null)
{
DeviceName = serviceInfo.Name;
this.State = BluetoothConnectionState.Connecting;
try
{
// Initialize the target Bluetooth RFCOMM device service
connectService = RfcommDeviceService.FromIdAsync(serviceInfo.Id);
rfcommService = await connectService;
if (rfcommService != null)
{
// Create a socket and connect to the target
socket = new StreamSocket();
connectAction = socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName, SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
State = BluetoothConnectionState.Connected;
Task taskReceive = Task.Run(async () => { ListenForMessagesAsync(socket); });
taskReceive.ConfigureAwait(false);
}
else
OnExceptionOccuredEvent(this, new Exception("Unable to create service.\nMake sure that the 'bluetooth.rfcomm' capability is declared with a function of type 'name:serialPort' in Package.appxmanifest."));
}
catch (TaskCanceledException)
{
this.State = BluetoothConnectionState.Disconnected;
}
catch (Exception ex)
{
this.State = BluetoothConnectionState.Disconnected;
OnExceptionOccuredEvent(this, ex);
}
}
}
And the listener in
private async Task ListenForMessagesAsync(StreamSocket localsocket)
{
while (socket != null)
{
try
{
string message = String.Empty;
DataReader dataReader = new DataReader(localsocket.InputStream);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
// Read the message and process it.
lock (this.interlock)
{
if (!message.Contains("\r\n"))
readBuffer = readBuffer + message;
else
{
var data = message.Split('\r');
readBuffer = readBuffer + data[0];
}
if (readBuffer.Length == 15)
{
readBuffer = readBuffer.Replace("\r\n", "");
OnMessageReceivedEvent(this, readBuffer);
readBuffer = String.Empty;
}
if (readBuffer.Length > 15 || (readBuffer.Length < 15 && readBuffer.Contains("\r\n")))
readBuffer = String.Empty;
}
}
catch (Exception ex)
{
if (socket != null)
OnExceptionOccuredEvent(this, ex);
}
}
}

Categories