Below is one method that basically sends the data to TCP Server.
UPDATE BEGINS HERE:
//////////////////////////////////
private string FormatValueByPresentation(IBuffer buffer, GattPresentationFormat format)
{
// BT_Code: For the purpose of this sample, this function converts only UInt32 and
// UTF-8 buffers to readable text. It can be extended to support other formats if your app needs them.
byte[] data;
CryptographicBuffer.CopyToByteArray(buffer, out data);
if (format != null)
{
if (format.FormatType == GattPresentationFormatTypes.UInt32 && data.Length >= 4)
{
return BitConverter.ToInt32(data, 0).ToString();
}
else if (format.FormatType == GattPresentationFormatTypes.Utf8)
{
try
{
return Encoding.UTF8.GetString(data);
}
catch (ArgumentException)
{
return "(error: Invalid UTF-8 string)";
}
}
else
{
// Add support for other format types as needed.
return "Unsupported format: " + CryptographicBuffer.EncodeToHexString(buffer);
}
}
else if (data != null)
{
// We don't know what format to use. Let's try some well-known profiles, or default back to UTF-8.
if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.HeartRateMeasurement))
{
try
{
///////LOOK HERE/////
**string b = ParseHeartRateValue(data).ToString();
TrySend(b);
//return "Heart Rate: " + ParseHeartRateValue(data).ToString();
return "Heart Rate: " + b;**
}
catch (ArgumentException)
{
return "Heart Rate: (unable to parse)";
}
}
else if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.BatteryLevel))
{
try
{
// battery level is encoded as a percentage value in the first byte according to
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml
return "Battery Level: " + data[0].ToString() + "%";
}
catch (ArgumentException)
{
return "Battery Level: (unable to parse)";
}
}
// This is our custom calc service Result UUID. Format it like an Int
else if (selectedCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
{
return BitConverter.ToInt32(data, 0).ToString();
}
// No guarantees on if a characteristic is registered for notifications.
else if (registeredCharacteristic != null)
{
// This is our custom calc service Result UUID. Format it like an Int
if (registeredCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
{
return BitConverter.ToInt32(data, 0).ToString();
}
}
else
{
try
{
return "Unknown format: " + Encoding.UTF8.GetString(data);
}
catch (ArgumentException)
{
return "Unknown format";
}
}
}
else
{
return "Empty data received";
}
return "Unknown format";
}
///////// END OF UPDATE //////
private async void TrySend(string data)
{
// Create the StreamSocket and establish a connection to the echo server.
StreamSocket socket = new StreamSocket();
try
{
var streamSocket = new Windows.Networking.Sockets.StreamSocket();
{
//The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("127.0.0.1");
await streamSocket.ConnectAsync((new Windows.Networking.HostName("127.0.0.1")), "9999");
// Send a request to the echo server.
using (Stream outputStream = streamSocket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
while (true)
{
await streamWriter.WriteLineAsync(data);
await streamWriter.FlushAsync();
}
//await streamWriter.WriteLineAsync(data);
//await streamWriter.FlushAsync();
}
}
}
}
catch (Exception)
{
}
}
And here is my TCP Server code that receives the data:
public class EchoServer {
public static void Main() {
TcpListener listener = null;
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9999);
listener.Start();
Console.WriteLine("TCP Server Has Started....");
while (true)
{
Console.WriteLine(" ");
Console.WriteLine("Waiting for incoming client connections....");
Console.WriteLine(" ");
Console.WriteLine("A message will display below once the client starts and establishes a connection ");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine(" ");
Console.WriteLine("Okay, Accepting Client connection now");
Console.WriteLine(" ");
Console.WriteLine("Accepted new client connection.....");
StreamReader reader = new StreamReader(client.GetStream());
StreamWriter writer = new StreamWriter(client.GetStream());
string s = string.Empty;
while (!(s = reader.ReadLine()).Equals("Exit") || (s == null)) {
Console.WriteLine("From client -> " + s);
writer.WriteLine("From server -> " + s);
writer.Flush();
}
reader.Close();
writer.Close();
client.Close();
}
} catch (Exception e)
{
Console.WriteLine(e);
} finally
{
if (listener != null)
{
listener.Stop();
}
}
}
}
Now, the data I am trying to get are the heart rates and it changes every two seconds. However on TCP server I only get the first recorded value of a heart rate and it keeps repeating instead of getting new one.
There is a similar post I saw here on stackoverflow : UWP TCP receive data continuously
and someone suggested to use while loop which I did as you can see in the code.
Are there any other suggestions on what should I do?
Thanks
while (true)
{
await streamWriter.WriteLineAsync(data);
await streamWriter.FlushAsync();
}
The while(true) will keep repeating, meaning that it will always send 'data' at its current value. This is what causes your issue.
In my opinion, you should keep a connection to your TCP server open outside of your 'TrySend' method, and use this method only to send the data. This way you won't need to use this loop.
EDIT :
Try this :
private async void CharacteristicReadButton_Click()
{
while(true)
{
// BT_Code: Read the actual value from the device by using Uncached.
GattReadResult result = await selectedCharacteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
if (result.Status == GattCommunicationStatus.Success)
{
string formattedResult = FormatValueByPresentation(result.Value, presentationFormat);
rootPage.NotifyUser($"Read result: {formattedResult}", NotifyType.StatusMessage);
//string formattedResult = FormatValueByPresentation(result.Value, presentationFormat);
//rootPage.NotifyUser($"Read result: {formattedResult}", NotifyType.StatusMessage);
}
else
{
rootPage.NotifyUser($"Read failed: {result.Status}", NotifyType.ErrorMessage);
}
}
}
Related
public class GSMModemConnection
{
I made a windows service for receiving SMS I'm using Wavecom Gsm modem 1306B. But after some second I got an
Exception: No data received from phone. I have searched in StackOverflow I found this link Exception: No data received from phone
someone pointed about to create a retry mechanism but I don't know how to implement it.
static void Main(string[] args)
{
GSMModemConnection gsm = new GSMModemConnection();
var result = gsm.OpenPort();
Console.WriteLine(result.PortName);
while (true)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ShortMessage execResult = gsm.ReadSMS(result, "AT+CMGL=\"ALL\"");
Console.WriteLine(execResult.Message);
Console.WriteLine(execResult.Status);
}
}
public class GSMModemConnection
{
public AutoResetEvent receiveNow;
//string strPortName, string strBaudRate
public SerialPort OpenPort()
{
receiveNow = new AutoResetEvent(false);
SerialPort port = new SerialPort();
port.PortName = "COM3";
port.BaudRate = 115200 /*Convert.ToInt32(strBaudRate)*/; //updated by Anila (9600)
port.DataBits = 8;
port.StopBits = StopBits.One;
port.Parity = Parity.None;
port.ReadTimeout = 300;
port.WriteTimeout = 300;
port.Encoding = Encoding.GetEncoding("iso-8859-1");
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
port.DtrEnable = true;
port.RtsEnable = true;
return port;
}
//Close Port
public void ClosePort(SerialPort port)
{
port.Close();
port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
port = null;
}
//Execute AT Command
public string ExecCommand(SerialPort port, string command, int responseTimeout, string errorMessage)
{
try
{
//receiveNow = new AutoResetEvent();
port.DiscardOutBuffer();
port.DiscardInBuffer();
receiveNow.Reset();
port.Write(command + "\r");
//Thread.Sleep(3000); //3 seconds
string input = ReadResponse(port, responseTimeout);
if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
throw new ApplicationException("No success message was received.");
return input;
}
catch (Exception ex)
{
throw new ApplicationException(errorMessage, ex);
}
}
//Receive data from port
public void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType == SerialData.Chars)
receiveNow.Set();
}
public string ReadResponse(SerialPort port, int timeout)
{
string buffer = string.Empty;
do
{
if (receiveNow.WaitOne(timeout, false))
{
string t = port.ReadExisting();
buffer += t;
}
else
{
if (buffer.Length > 0)
throw new ApplicationException("Response received is incomplete.");
else
throw new ApplicationException("No data received from phone.");
}
}
while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
return buffer;
}
public ShortMessage ReadSMS(SerialPort port, string p_strCommand)
{
// Set up the phone and read the messages
ShortMessage messages = null;
try
{
#region Execute Command
// Check connection
var a = ExecCommand(port, "AT", 300, "No phone connected");
// Use message format "Text mode"
var b = ExecCommand(port, "AT+CMGF=1\r", 300, "Failed to set message format.");
// Use character set "PCCP437"
//var c = ExecCommand(port, "AT+CSCS=\"PCCP437\"", 300, "Failed to set character set.");
// Select SIM storage
//var d = ExecCommand(port, "AT+CPMS=\"SM\"", 300, "Failed to select message storage.");
// Read the messages
string input = ExecCommand(port, p_strCommand, 5000, "Failed to read the messages.");
#endregion
#region Parse messages
messages = ParseMessages(input);
#endregion
}
catch (Exception ex)
{
throw ex;
}
if (messages != null)
return messages;
else
return null;
}
public ShortMessage ParseMessages(string input)
{
ShortMessage msg = new ShortMessage();
//ShortMessageCollection messages = new ShortMessageCollection();
try
{
Regex r = new Regex(#"\+CMGL: (\d+),""(.+)"",""(.+)"",(.*),""(.+)""\r\n(.+)\r\n");
Match m = r.Match(input);
while (m.Success)
{
//msg.Index = int.Parse(m.Groups[1].Value);
msg.Index = int.Parse(m.Groups[1].Value);
msg.Status = m.Groups[2].Value;
msg.Sender = m.Groups[3].Value;
msg.Alphabet = m.Groups[4].Value;
msg.Sent = m.Groups[5].Value;
msg.Message = m.Groups[6].Value;
//messages.Add(msg);
m = m.NextMatch();
}
}
catch (Exception ex)
{
throw ex;
}
return msg;
}
}
You DO have a retry mechanism in place with the DO-WHILE loop from the following expression:
public string ReadResponse(SerialPort port, int timeout)
{
string buffer = string.Empty;
do
{
if (receiveNow.WaitOne(timeout, false))
{
string t = port.ReadExisting();
buffer += t;
}
else
{
if (buffer.Length > 0)
throw new ApplicationException("Response received is incomplete.");
else
throw new ApplicationException("No data received from phone.");
}
}
while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
return buffer;
}
What you can do is reduce the granularity of the error report mechanism.
In this case, for example, the line throw new ApplicationException() will break the loop and get captured in the Exec() function, then thrown again. If you just want to wait for the loop to close and the DO-WHILE loop to get executed, I would replace the following part of the code:
else
{
if (buffer.Length > 0)
throw new ApplicationException("Response received is incomplete.");
else
throw new ApplicationException("No data received from phone.");
}
With:
else
{
if (buffer.Length > 0)
bufferError += "Response received is incomplete.\n";
else
bufferError += "No data received from phone.\n";
}
Why?
After the DO-WHILE loop, the buffer will either return string.Empty or some value.
Up in the code, you have:
string input = ReadResponse(port, responseTimeout);
if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
throw new ApplicationException("No success message was received.");
return input;
Basically, if the returned buffer is string.Empty an Exception will be thrown again.
By returning the buffer Error, you can decide later how you want to handle it but the DO-WHILE loop will run at least once up until the condition from the WHILE expression is met.
Rest of the code should look like this:
//Thread.Sleep(3000); //3 seconds
string bufferErrors = string.Empty;
string input = ReadResponse(port, responseTimeout, bufferErrors);
if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
throw new ApplicationException("No success message was received.");
if (!string.IsNullOrWhiteSpace(bufferErrors))
{
//Handle bufferErrors
}
return input;
Remember to declare the out parameter in ReadResponse
public string ReadResponse(SerialPort port, int timeout, out string bufferError)
{
string buffer = string.Empty;
bufferError = string.Empty;
I'm trying to connect this IMU sensor via bluetooth to a Unity implemented application that currently is running in the Unity Editor on a Windows laptop and eventually will be built for Android.
Here I found some example code (project: LpSensorCSharp_CommandLine_Example) which I tested successfully: The output on the console is the desired sensor data. As you can see the connection process is pretty straightforward:
string sensor1Address = "00:04:3E:9B:A3:55";
LpmsB2 sensor1 = new LpmsB2(sensor1Address);
// connects to sensor
int retryCount = 0;
sensor1.connect();
while (sensor1.getConnectionStatus() != LpmsB2.SENSOR_STATUS_CONNECTED)
{
if (sensor1.getConnectionStatus() == LpmsB2.SENSOR_STATUS_ERROR)
{
if (retryCount >= 3)
{
Console.WriteLine("Error connecting to sensor after {0} retries. Terminating bye", retryCount);
System.Environment.Exit(1);
}
Console.WriteLine("Error connecting to sensor: {0}", sensor1.getErrorMessage());
Console.WriteLine("retrying {0}", retryCount);
retryCount += 1;
}
Thread.Sleep(3000);
}
sensor1.printConfig();
String filename = "LpmsB2Log_"+DateTime.Now.ToString("yyyyMMddHHmmss") + ".csv";
using (StreamWriter writetext = new StreamWriter(filename))
{
while (sensor1.getConnectionStatus() == LpmsB2.SENSOR_STATUS_CONNECTED)
{
int nData = sensor1.hasNewData();
if (nData > 0)
{
LpmsData sd = new LpmsData();
// Here we have our desired data
sd = ((LpmsData)sensor1.getLpmsBData());
}
else
System.Threading.Thread.Sleep(1);
}
}
Now I'm trying to implement the same functionality in Unity, which looks like that (OnConnect is listening to a button's OnClick):
private void OnConnect()
{
StartCoroutine(Connect());
}
private IEnumerator Connect()
{
sensor1 = new LpmsB2(sensorAddress);
// connects to sensor
int retryCount = 0;
sensor1.connect();
while (sensor1.getConnectionStatus() != LpmsB2.SENSOR_STATUS_CONNECTED)
{
Debug.LogFormat("Connection status: {0}", sensor1.getConnectionStatus());
if (retryCount >= 3)
{
Debug.LogFormat("Error connecting to sensor after {0} retries. Terminating bye", retryCount);
break;
}
Debug.LogFormat("Error connecting to sensor: {0}", sensor1.getErrorMessage());
Debug.LogFormat("retrying {0}", retryCount);
retryCount += 1;
yield return new WaitForSeconds(3f);
}
}
void Update()
{
if(sensor1 != null)
{
Debug.LogFormat("Connection status: {0}", sensor1.getConnectionStatus());
if (sensor1.getConnectionStatus() == LpmsB2.SENSOR_STATUS_CONNECTED)
{
int nData = sensor1.hasNewData();
if (nData > 0)
{
LpmsData sd = new LpmsData();
sd = ((LpmsData)sensor1.getLpmsBData());
// ...
}
}
}
}
The problem is that sensor1.getConnectionStatus() beginning with its first call always returns LpmsB2.SENSOR_STATUS_CONNECTING, whereas sensor1.getErrorMessage() always returns "none" during the Connect. Could you please give me indication what I'm doing wrong in my Unity implementation?
Here is how my Client connects to the server:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Net.Sockets;
using System.IO;
using System;
using System.Text.RegularExpressions;
using UnityEngine.SceneManagement;
using Newtonsoft.Json;
using System.Linq;
public class ClientWorldServer : MonoBehaviour {
public bool socketReady;
public static TcpClient socket;
public static NetworkStream stream;
public static StreamWriter writer;
public static StreamReader reader;
public void ConnectToWorldServer()
{
if (socketReady)
{
return;
}
//Default host and port values;
string host = "127.0.0.1";
int port = 8080;
try
{
socket = new TcpClient(host, port);
stream = socket.GetStream();
writer = new StreamWriter(stream);
reader = new StreamReader(stream);
socketReady = true;
}
catch (Exception e)
{
Debug.Log("Socket error : " + e.Message);
}
}
}
Here is how i send data to the server using my Send function:
public void Send(string header, Dictionary<string, string> data)
{
if (stream.CanRead)
{
socketReady = true;
}
if (!socketReady)
{
return;
}
JsonData SendData = new JsonData();
SendData.header = "1x" + header;
foreach (var item in data)
{
SendData.data.Add(item.Key.ToString(), item.Value.ToString());
}
SendData.connectionId = connectionId;
string json = JsonConvert.SerializeObject(SendData);
var howManyBytes = json.Length * sizeof(Char);
writer.WriteLine(json);
writer.Flush();
Debug.Log("Client World:" + json);
}
As you can see i'm sending the data to the Stream like a string not like a byte array. As far as i know i should send the data as byte array prepending the size of the message and following the message. On the server side i have no clue how i can read that data.
Here is how i read it now(it works for now, but it will not work if i try to send more messages at once):
class WorldServer
{
public List<ServerClient> clients = new List<ServerClient>();
public List<ServerClient> disconnectList;
public List<CharactersOnline> charactersOnline = new List<CharactersOnline>();
public int port = 8080;
private TcpListener server;
private bool serverStarted;
private int connectionIncrementor;
private string mysqlConnectionString = #"server=xxx;userid=xxx;password=xxx;database=xx";
private MySqlConnection mysqlConn = null;
private MySqlDataReader mysqlReader;
static void Main(string[] args)
{
WorldServer serverInstance = new WorldServer();
Console.WriteLine("Starting World Server...");
try
{
serverInstance.mysqlConn = new MySqlConnection(serverInstance.mysqlConnectionString);
serverInstance.mysqlConn.Open();
Console.WriteLine("Connected to MySQL version: " + serverInstance.mysqlConn.ServerVersion + "\n");
}
catch (Exception e)
{
Console.WriteLine("MySQL Error: " + e.ToString());
}
finally
{
if (serverInstance.mysqlConn != null)
{
serverInstance.mysqlConn.Close();
}
}
serverInstance.clients = new List<ServerClient>();
serverInstance.disconnectList = new List<ServerClient>();
try
{
serverInstance.server = new TcpListener(IPAddress.Any, serverInstance.port);
serverInstance.server.Start();
serverInstance.StartListening();
serverInstance.serverStarted = true;
Console.WriteLine("Server has been started on port: " + serverInstance.port);
}
catch (Exception e)
{
Console.WriteLine("Socket error: " + e.Message);
}
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
/* run your code here */
while (true)
{
string input = Console.ReadLine();
string[] commands = input.Split(':');
if (commands[0] == "show online players")
{
Console.WriteLine("Showing connections\n");
foreach (CharactersOnline c in serverInstance.charactersOnline)
{
Console.WriteLine("Character name: " + c.characterName + "Character ID: " + c.characterId + "Connection id: " + c.connectionId + "\n");
}
}
continue;
}
}).Start();
while (true)
{
serverInstance.Update();
}
}
private void Update()
{
//Console.WriteLine("Call");
if (!serverStarted)
{
return;
}
foreach (ServerClient c in clients.ToList())
{
// Is the client still connected?
if (!IsConnected(c.tcp))
{
c.tcp.Close();
disconnectList.Add(c);
Console.WriteLine(c.connectionId + " has disconnected.");
CharacterLogout(c.connectionId);
continue;
//Console.WriteLine("Check for connection?\n");
}
else
{
// Check for message from Client.
NetworkStream s = c.tcp.GetStream();
if (s.DataAvailable)
{
StreamReader reader = new StreamReader(s, true);
string data = reader.ReadLine();
if (data != null)
{
OnIncomingData(c, data);
}
}
//continue;
}
}
for (int i = 0; i < disconnectList.Count - 1; i++)
{
clients.Remove(disconnectList[i]);
disconnectList.RemoveAt(i);
}
}
private void OnIncomingData(ServerClient c, string data)
{
Console.WriteLine(data);
dynamic json = JsonConvert.DeserializeObject(data);
string header = json.header;
//Console.WriteLine("Conn ID:" + json.connectionId);
string connId = json.connectionId;
int.TryParse(connId, out int connectionId);
string prefix = header.Substring(0, 2);
if (prefix != "1x")
{
Console.WriteLine("Unknown packet: " + data + "\n");
}
else
{
string HeaderPacket = header.Substring(2);
switch (HeaderPacket)
{
default:
Console.WriteLine("Unknown packet: " + data + "\n");
break;
case "004":
int accountId = json.data["accountId"];
SelectAccountCharacters(accountId, connectionId);
break;
case "005":
int characterId = json.data["characterId"];
getCharacterDetails(characterId, connectionId);
break;
case "006":
int charId = json.data["characterId"];
SendDataForSpawningOnlinePlayers(charId, connectionId);
break;
case "008":
Dictionary<string, string> dictObj = json.data.ToObject<Dictionary<string, string>>();
UpdateCharacterPosition(dictObj, connectionId);
break;
}
}
private bool IsConnected(TcpClient c)
{
try
{
if (c != null && c.Client != null && c.Client.Connected)
{
if (c.Client.Poll(0, SelectMode.SelectRead))
{
return !(c.Client.Receive(new byte[1], SocketFlags.Peek) == 0);
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void StartListening()
{
server.BeginAcceptTcpClient(OnConnection, server);
}
private void OnConnection(IAsyncResult ar)
{
connectionIncrementor++;
TcpListener listener = (TcpListener)ar.AsyncState;
clients.Add(new ServerClient(listener.EndAcceptTcpClient(ar)));
clients[clients.Count - 1].connectionId = connectionIncrementor;
StartListening();
//Send a message to everyone, say someone has connected!
Dictionary<string, string> SendDataBroadcast = new Dictionary<string, string>();
SendDataBroadcast.Add("connectionId", clients[clients.Count - 1].connectionId.ToString());
Broadcast("001", SendDataBroadcast, clients, clients[clients.Count - 1].connectionId);
Console.WriteLine(clients[clients.Count - 1].connectionId + " has connected.");
}
And this is how the server send back data to the client:
private void Send(string header, Dictionary<string, string> data, int cnnId)
{
foreach (ServerClient c in clients.ToList())
{
if (c.connectionId == cnnId)
{
try
{
//Console.WriteLine("Sending...");
StreamWriter writer = new StreamWriter(c.tcp.GetStream());
if (header == null)
{
header = "000";
}
JsonData SendData = new JsonData();
SendData.header = "0x" + header;
foreach (var item in data)
{
SendData.data.Add(item.Key.ToString(), item.Value.ToString());
}
SendData.connectionId = cnnId;
string JSonData = JsonConvert.SerializeObject(SendData);
writer.WriteLine(JSonData);
writer.Flush();
//Console.WriteLine("Trying to send data to connection id: " + cnnId + " data:" + sendData);
}
catch (Exception e)
{
Console.WriteLine("Write error : " + e.Message + " to client " + c.connectionId);
}
}
}
}
Here is my ServerClient class:
public class ServerClient
{
public TcpClient tcp;
public int accountId;
public int connectionId;
public ServerClient(TcpClient clientSocket)
{
tcp = clientSocket;
}
}
Can you please show me how i should modify my Send function on the client to send the data as byte array so i can create "TCP Message Framing" and how should i change my the following part on the server:
foreach (ServerClient c in clients.ToList())
{
// Is the client still connected?
if (!IsConnected(c.tcp))
{
c.tcp.Close();
disconnectList.Add(c);
Console.WriteLine(c.connectionId + " has disconnected.");
CharacterLogout(c.connectionId);
continue;
//Console.WriteLine("Check for connection?\n");
}
else
{
// Check for message from Client.
NetworkStream s = c.tcp.GetStream();
if (s.DataAvailable)
{
StreamReader reader = new StreamReader(s, true);
string data = reader.ReadLine();
if (data != null)
{
OnIncomingData(c, data);
}
}
//continue;
}
}
which is responsible for receving data on the server ?
Is it possible to change only these parts from the Client and on the Server and make it continue to work but this time properly with TCP Message Framing ?
Of course the listener on the client and the Send function of the server i'll remake once i understand how this framing should look like.
Your frames are already defined by cr/lf - so that much already exists; what you need to do is to keep a back buffer per stream - something like a MemoryStream might be sufficient, depending on how big you need to scale; then essentially what you're looking to do is something like:
while (s.DataAvailable)
{
// try to read a chunk of data from the inbound stream
int bytesRead = s.Read(someBuffer, 0, someBuffer.Length);
if(bytesRead > 0) {
// append to our per-socket back-buffer
perSocketStream.Position = perSocketStream.Length;
perSocketStream.Write(someBuffer, 0, bytesRead);
int frameSize; // detect any complete frame(s)
while((frameSize = DetectFirstCRLF(perSocketStream)) >= 0) {
// decode it as text
var backBuffer = perSocketStream.GetBuffer();
string message = encoding.GetString(
backBuffer, 0, frameSize);
// remove the frame from the start by copying down and resizing
Buffer.BlockCopy(backBuffer, frameSize, backBuffer, 0,
(int)(backBuffer.Length - frameSize));
perSocketStream.SetLength(backBuffer.Length - frameSize);
// process it
ProcessMessage(message);
}
}
}
I'm sorry if I'm asking something asked before.
I'm developing a program that reads data received via TCP and using a StreamReader, and I just can't find how to make sure that any data won't be missed. Is there any way to create a middle buffer to read from there or something like that?
Here are the methods I've created for receiving data and write it to a text box:
public static void Connect(string IP, string port)
{
try
{
client = new TcpClient();
IPEndPoint IP_End = new IPEndPoint(IPAddress.Parse(IP), int.Parse(port));
client.Connect(IP_End);
if (client.Connected)
{
connected = "Connected to Exemys!" + "\r\n";
STR = new StreamReader(client.GetStream());
bgWorker = true;
}
}
catch (Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
-
public static void MessageReceiving(TextBox textBox)
{
try
{
string values =Conection.STR.ReadLine();
textBox.Invoke(new MethodInvoker(delegate () { textBox.AppendText("Exemys : " + values.Substring(2) + Environment.NewLine); }));
try
{
string messagetype = values.Substring(5, 1);
string ID = values.Substring(3, 2);
string checksum = values.Substring(values.Length - 2, 2);
if (checksum == CalcularChecksum(values.Substring(3, values.Length - 5)))
{
if (messagetype == "N")
{
if (ID == "01")
{
ID1 = values.Substring(3, 2);
messagetype1 = values.Substring(5, 1);
capacity1 = values.Substring(6, 1);
pressure1 = values.Split(',')[1];
sequencetime1 = values.Split(',')[2];
runstatus1 = values.Split(',')[3];
mode1 = values.Split(',')[4].Substring(0, 1);
checksum1 = CalcularChecksum(values.Substring(3, values.Length - 5));
}
if (ID == "02")
{
ID2 = values.Substring(3, 2);
messagetype2 = values.Substring(5, 1);
capacity2 = values.Substring(6, 1);
pressure2 = values.Split(',')[1];
sequencetime2 = values.Split(',')[2];
runstatus2 = values.Split(',')[3];
mode2 = values.Split(',')[4].Substring(0, 1);
checksum2 = CalcularChecksum(values.Substring(3, values.Length - 5));
}
}
}
}
catch(Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
catch (Exception)
{
MessageBox.Show("Client disconnected.");
}
}
Edit: what I'm trying to ask is how to always process the entire data before continue receiving? That would be the question.
A TCP stream is a stream of bytes that ends when the socket is closed by you or the remote peer or breaks because of network issues. In order to get everything from the stream you need to call the StreamReader.ReadLine method inside a loop into a buffer until some stop condition applies.
...
try
{
while(true)
{
...
input = STR.ReadLine();
if (input == <some stop condition>)
break;
...
}
}
...
That's a highly simplified example. TCP reading with partial buffer handling can be a complex beast so I recommend to use a library or framework if you're doing more than some hobby project.
Thanks for the response, but after searching, I've found what I was looking for. I wanted to store those messages (data) that were entering to make sure that I won't lose them (for any reason, more precisely that the receiving process would be faster than the message processing operation), so I used Queue to achieve this.
public static void RecepcionMensajes(TextBox textBox)
{
if (client.Connected == true)
{
try
{
string fifo = Conexion.STR.ReadLine();
Queue mensajes = new Queue();
//AquĆ se ponen en cola los mensajes que van llegando, utilizando el sistema FIFO.
mensajes.Enqueue(fifo);
string values = mensajes.Dequeue().ToString();
textBox.Invoke(new MethodInvoker(delegate () { textBox.AppendText("Exemys : " + values.Substring(2) + Environment.NewLine); }));
I am currently making a client and server. The server will store people and their location using a dictionary. The client can then lookup a location or update/add a person and their location.
For example, I could type 'Lucy', 'School', and the server will add that to the dictionary. If I then type 'Lucy' it should reply with 'School' and if I type in 'Lucy' 'Home' it should up date that to the dictionary.
Currently however, it is not adding people to the dictionary or updating their location. The following is my code to run the server, which is called in the main class:
static void runServer()
{
TcpListener listener;
Socket connection;
NetworkStream socketStream;
try
{
listener = new TcpListener(IPAddress.Any, 43);
listener.Start();
Console.WriteLine("Server started listening");
while (true)
{
connection = listener.AcceptSocket();
socketStream = new NetworkStream(connection);
Console.WriteLine("Connection Recieved.");
doRequest(socketStream);
socketStream.Close();
connection.Close();
}
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.ToString());
}
}
This part is the doRequest method which is called in the runServer. This is where I have made my dictionary and tried methods to update and add:
static void doRequest(NetworkStream socketStream)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
try
{
socketStream.ReadTimeout = 1000;
socketStream.WriteTimeout = 1000;
StreamWriter sw = new StreamWriter(socketStream);
StreamReader sr = new StreamReader(socketStream);
String line = sr.ReadLine().Trim();
String[] Sections = line.Split(new char[] {' '}, 2);
if (Sections.Length == 1)
{
if (dictionary.ContainsKey(Sections[0]))
{
sw.WriteLine(dictionary[Sections[0]]);
sw.Flush();
}
else
{
sw.WriteLine("ERROR: no entries found");
sw.Flush();
}
}
else if (Sections.Length == 2)
{
try
{
if (dictionary.ContainsKey(Sections[0]))
{
dictionary[Sections[0]] = Sections[1];
sw.WriteLine("OK");
sw.Flush();
}
else
{
dictionary.Add(Sections[0], Sections[1]);
sw.WriteLine("OK");
sw.Flush();
}
}
catch
{
sw.WriteLine("DIDNT SAVE");
}
}
else
{
sw.WriteLine("Too many arguments");
}
}
catch
{
Console.WriteLine("Unable to connect");
}
}
I've tried various different ways. My first concern was that the dictionary is created in the dorequest method so isn't global, however it doesn't seem to work when I put it somewhere else.
Hopefully it's just a little mistake I've missed.
Any help would be largely appreciated.
Thanks
Lucy