I have written a socket server application which listens and provides services to other processes, in the server application the part that listens for requests:
void ListenToClients() {
while (true) {
IPEndPoint ipEP = new IPEndPoint(IPAddress.Any
, mcintPortRequests);
TcpListener listener = new TcpListener(ipEP);
try {
dicParams_t dicParams = null;
dicRegEx_t dicRegEx = null;
listener.Start();
if (mclient != null) {
mclient.Close();
}
mclient = listener.AcceptTcpClient();
mstream = mclient.GetStream();
if (mstream == null && !mstream.CanRead) {
listener.Stop();
return;
}
if (mstream.Read(msarybytRX, 0, msarybytRX.Length) == 0) {
return;
}
string strRX = Encoding.UTF8.GetString
(msarybytRX, 0, msarybytRX.Length);
//Trim off any null terminators
int intNT = strRX.IndexOf("\0");
if (intNT >= 0) {
strRX = strRX.Substring(0, intNT);
}
bool blnHTTPget, blnHTTPpost;
blnHTTPget = blnHTTPpost = false;
if (strRX.StartsWith(mcstrGET)) {
//Remove any framming information
const string cstrGetStartOfParams = "/?";
strRX = strRX.Substring(mcstrGET.Length);
int intStartOfParams = strRX
.IndexOf(cstrGetStartOfParams);
if (intStartOfParams >= 0) {
const string cstrGetEndOfParams = " HTTP/";
strRX = strRX.Substring(intStartOfParams
+ cstrGetStartOfParams.Length);
int intEODofParams = strRX.IndexOf
(cstrGetEndOfParams);
if (intEODofParams >= 0) {
strRX = strRX.Substring(0
, intEODofParams);
blnHTTPget = true;
}
}
} else if (strRX.StartsWith(mcstrPOST)) {
const string cstrPostStartOfParams = "\r\n\r\n";
strRX = strRX.Substring(mcstrPOST.Length);
int intStartOfParams = strRX
.IndexOf(cstrPostStartOfParams);
if (intStartOfParams >= 0) {
const string cstrPostEndOfParams = "\0";
strRX = strRX.Substring(intStartOfParams
+ cstrPostStartOfParams.Length);
int intEODofParams = strRX.IndexOf
(cstrPostEndOfParams);
if (intEODofParams >= 0) {
strRX = strRX.Substring(0
, intEODofParams);
blnHTTPpost = true;
}
}
}
string[] arystrParams = null;
if (blnHTTPget || blnHTTPpost) {
const char cchrParamDelimiter = '&';
arystrParams = strRX.Split
(cchrParamDelimiter);
} else {
//Remove whitespace
string strJSON = string.Empty;
bool blnInString = false;
for (int i = 0; i < strRX.Length; i++) {
if (strRX[i] == '\''
|| strRX[i] == '\"') {
blnInString = !blnInString;
} else if (blnInString == false
&& (strRX[i] == '\n'
|| strRX[i] == '\r'
|| strRX[i] == '\t'
|| strRX[i] == ' ')) {
continue;
}
strJSON += strRX[i];
}
//Not HTML GET or POST, is this a directy JSON
//exchange?
JsonObject jsonRX = JsonSerializer
.Deserialize(strJSON);
if (jsonRX != null) {
arystrParams = jsonRX
.Select(kvp => $"{kvp.Key}={kvp.Value}")
.ToArray();
}
}
if (arystrParams != null
&& arystrParams.Length > 0) {
dicParams = new dicParams_t();
dicRegEx = new dicRegEx_t();
foreach (string strParam in arystrParams) {
const char cchrAssignDelimiter = '=';
string[] arystrParam = strParam.Split
(cchrAssignDelimiter);
if (arystrParam.Length > 0) {
dicParams.Add(arystrParam[0]
, arystrParam[1]);
}
}
if (mfrmSetup == null) {
mfrmSetup = new clsSetupForm();
}
//Ensure the table is clear
mfrmSetup.table.Controls.Clear();
mfrmSetup.table.ColumnStyles.Clear();
mfrmSetup.table.RowStyles.Clear();
if (dicParams.Count > 0
&& mfrmSetup != null
&& mfrmSetup.Visible != true
&& dicParams.ContainsKey(mcstrFormTitle)) {
mfrmSetup.AcceptButton = mfrmSetup.OK;
mfrmSetup.CancelButton = mfrmSetup.Cancel;
foreach (kvpParams_t kvPair in dicParams) {
if (kvPair.Key
.CompareTo(mcstrFormTitle) == 0) {
mfrmSetup.Text = kvPair.Value;
continue;
}
if (kvPair.Key
.CompareTo(mcstrControls) != 0) {
continue;
}
//Translate JSON string into controls array
JsonArray aryControls = JsonSerializer
.Deserialize
(kvPair.Value);
//Process each record of the controls array
int intRow = 0;
foreach (JsonObject objCtrl in aryControls) {
string strLabel, strRegEx, strType;
JsonObject objParams = null;
strLabel = strRegEx = strType = null;
if (objCtrl[mcstrLabel] != null) {
strLabel = objCtrl[mcstrLabel]
.ToString();
}
if (!string.IsNullOrEmpty(strLabel)
&& objCtrl[mcstrRegEx] != null) {
strRegEx = objCtrl[mcstrRegEx]
.ToString();
dicRegEx.Add(strLabel, strRegEx);
}
if (objCtrl[mcstrParams] != null) {
objParams = JsonSerializer
.Deserialize
(objCtrl[mcstrParams]);
}
if (objCtrl[mcstrType] != null) {
strType = objCtrl[mcstrType]
.ToString();
}
if (string.IsNullOrEmpty(strLabel)
|| string.IsNullOrEmpty(strType)) {
continue;
}
//Create and add label to table
Label lblKey = new Label();
lblKey.Text = strLabel + ":";
lblKey.TextAlign = ContentAlignment.MiddleRight;
mfrmSetup.table.Controls.Add(lblKey
, mcintColumnLabels, intRow);
mfrmSetup.table.ColumnStyles.Add
(new ColumnStyle(SizeType.Absolute
, lblKey.Width));
mfrmSetup.table.RowStyles.Add
(new RowStyle(SizeType.Absolute
, lblKey.Height));
//Create and add control to table
CheckBox cbCtrl = null;
ListBox lbCtrl = null;
TextBox tbCtrl = null;
Control ctrl = null;
if (strType.CompareTo(mcstrCtrlCheckBox) == 0) {
cbCtrl = new CheckBox();
if (cbCtrl != null) {
ctrl = cbCtrl;
}
} else if (strType.CompareTo(mcstrCtrlListBox) == 0) {
lbCtrl = new ListBox();
if (lbCtrl != null) {
ctrl = lbCtrl;
}
} else if (strType.CompareTo(mcstrCtrlTextBox) == 0) {
tbCtrl = new TextBox();
if (tbCtrl != null) {
if (!string.IsNullOrEmpty(strRegEx)) {
tbCtrl.TextChanged += TextBoxChanged;
tbCtrl.Tag = strRegEx;
}
ctrl = tbCtrl;
}
}
if (ctrl != null) {
//Add control to table
mfrmSetup.table.Controls.Add(ctrl
, mcintColumnControls, intRow);
intRow++;
ctrl.Anchor = AnchorStyles.None;
//Apply parameters
foreach (var jsonNode in objParams) {
string strKey = jsonNode.Key;
int intTmp;
if (strKey.CompareTo(mcstrName) == 0) {
//Set name first, it helps to identify control when debugging
ctrl.Name = jsonNode.Value.ToString();
continue;
}
if (strKey.CompareTo(mcstrCheckAlign) == 0
&& cbCtrl != null) {
string strCheckAlign = jsonNode.Value
.ToString();
if (strCheckAlign
.CompareTo(mcstrCenter) == 0) {
cbCtrl.CheckAlign = ContentAlignment
.MiddleCenter;
} else if (strCheckAlign
.CompareTo(mcstrLeft) == 0) {
cbCtrl.CheckAlign = ContentAlignment
.MiddleLeft;
} else if (strCheckAlign
.CompareTo(mcstrRight) == 0) {
cbCtrl.CheckAlign = ContentAlignment
.MiddleRight;
}
continue;
}
if (strKey.CompareTo(mcstrHeight) == 0) {
if (int.TryParse(jsonNode.Value.ToString()
, out intTmp)) {
ctrl.Height = intTmp;
}
continue;
}
if (strKey.CompareTo(mcstrTextAlign) == 0
&& tbCtrl != null) {
string strTextAlign = jsonNode.Value
.ToString();
if (strTextAlign
.CompareTo(mcstrCenter) == 0) {
tbCtrl.TextAlign = HorizontalAlignment
.Center;
} else if (strTextAlign
.CompareTo(mcstrLeft) == 0) {
tbCtrl.TextAlign = HorizontalAlignment
.Left;
} else if (strTextAlign
.CompareTo(mcstrRight) == 0) {
tbCtrl.TextAlign = HorizontalAlignment
.Right;
}
continue;
}
if (strKey.CompareTo(mcstrText) == 0) {
ctrl.Text = jsonNode.Value.ToString();
continue;
}
if (strKey.CompareTo(mcstrWidth) == 0) {
if (int.TryParse(jsonNode.Value.ToString()
, out intTmp)) {
ctrl.Width = intTmp;
}
continue;
}
}
}
}
}
DialogResult result = mfrmSetup.ShowDialog();
if (result != DialogResult.OK) {
continue;
}
Console.WriteLine("HERE!");
}
}
//Then, update the code to use JsonObject correctly:
JsonObject objResponse = new JsonObject();
MemoryStream memStream = new MemoryStream
(msarybytRX, 0, msarybytRX.Length);
StreamWriter streamWriter = new StreamWriter(memStream);
streamWriter.Flush();
Console.WriteLine(strRX);
} catch (Exception ex) {
Console.WriteLine($"ERROR: {ex.Message}");
} finally {
listener.Stop();
}
}
}
public Task Start() {
new Thread(async () => ListenToClients()).Start();
return Task.CompletedTask;
}
When the form is edited and more information is available to the client, the client will send additional information to the server which is received by a task that is created when the form text box is changed.
void TextBoxChanged(object obj, EventArgs args) {
TextBox tb = (TextBox)obj;
if (tb != null && !string.IsNullOrEmpty((string)tb.Tag)) {
string strRegEx = (string)tb.Tag;
if ((new Regex(strRegEx)).IsMatch(tb.Text)) {
tb.ForeColor = Color.Gray;
if (mstrLastSlot == null) {
mstrLastSlot = string.Empty;
}
if (tb.Text.CompareTo(mstrLastSlot) != 0) {
// Request information from Optix about this slot
if (mclient.Connected == true
&& mstream != null
&& mstream.CanWrite) {
int intSlot = 0;
if (!int.TryParse(tb.Text, out intSlot)
|| intSlot < 0) {
return;
}
JsonObject objTX = new JsonObject();
objTX[mcstrMsgCode] = mcuintMsgCodeRequestTags;
objTX[mcstrSlot] = intSlot;
string strTX = objTX.ToString();
byte[] arybytTX = Encoding.ASCII.GetBytes(strTX);
if (arybytTX != null) {
mstream.Write(arybytTX, 0, arybytTX.Length);
// Wait for response
const int cintWaitForResponseMS = 5000;
var WaitForResponse = Task.Run(() => {
JsonObject objRX;
try {
if (mstream == null && !mstream.CanRead) {
return;
}
if (mstream.Read(msarybytRX, 0, msarybytRX.Length) == 0) {
return;
}
objRX = JsonSerializer.Deserialize(msarybytRX);
if (objRX != null) {
Console.WriteLine("HACK");
}
} catch (Exception ex) {
Console.WriteLine($"ERROR: {ex.Message}");
}
});
if (WaitForResponse.Wait(cintWaitForResponseMS)) {
// Response received
} else {
throw new TimeoutException("The operation has timed out.");
}
}
}
}
} else {
tb.ForeColor = Color.Red;
}
}
}
The problem I am getting is an exception when receiving the second packet from the client:
{"'\"' is invalid after a single JSON value. Expected end of data. Path: $ | LineNumber: 0 | BytePositionInLine: 643."}
This is the client code that sends the packet that causes the above exception:
string strJSON = jsonTags.ToJsonString();
Console.WriteLine(strJSON);
byte[] arybytJSON = Encoding.UTF8.GetBytes(strJSON);
if (ssckBMLFS.Send(arybytJSON) > 0) {
...
From the console strJSON:
{"slot":3,"count":16,"Data":["00, Board Magazine Walker (LHS)\r\nSensor (String)","01, Board Magazine Walker (RHS)\r\nSensor (String)","02, Spare (String)","03, Board Magazine Low Level Sensor (String)","04, Spare (String)","05, Board Arm\r\nBrake Release\r\nButton (String)","06, Board Arm Servo Homing Sensor (String)","07, Spare (String)","08, Spare (String)","09, Spare (String)","10, Board Down Sensor (String)","11, Spare (String)","12, Bottom Flap Retainer Fully Up Sensor (String)","13, Spare (String)","14, Board Magazine Support (Auto) Selector Switch (String)","15, Board Magazine Support (Refill) Selector Switch (String)"]}
I've verified the above on https://jsonlint.com/ and the response is JSON is valid!
Can anyone see what could be wrong in the receiving task block?