Abnormally disconnected TCP sockets and write timeout - c++builder

I will try to explain the problem in shortest possible words. I am using c++ builder 2010.
I am using TIdTCPServer and sending voice packets to a list of connected clients. Everything works ok untill any client is disconnected abnormally, For example power failure etc. I can reproduce similar disconnect by cutting the ethernet connection of a connected client.
So now we have a disconnected socket but as you know it is not yet detected at server side so server will continue to try to send data to that client too.
But when server try to write data to that disconnected client ...... Write() or WriteLn() HANGS there in trying to write, It is like it is wating for somekind of Write timeout. This hangs the hole packet distribution process as a result creating a lag in data transmission to all other clients. After few seconds "Socket Connection Closed" Exception is raised and data flow continues.
Here is the code
try
{
EnterCriticalSection(&SlotListenersCriticalSection);
for(int i=0;i<SlotListeners->Count;i++)
{
try
{
//Here the process will HANG for several seconds on a disconnected socket
((TIdContext*) SlotListeners->Objects[i])->Connection->IOHandler->WriteLn("Some DATA");
}catch(Exception &e)
{
SlotListeners->Delete(i);
}
}
}__finally
{
LeaveCriticalSection(&SlotListenersCriticalSection);
}
Ok i already have a keep alive mechanism which disconnect the socket after n seconds of inactivity. But as you can imagine, still this mechnism cant sync exactly with this braodcasting loop because this braodcasting loop is running almost all the time.
So is there any Write timeouts i can specify may be through iohandler or something ? I have seen many many threads about "Detecting disconnected tcp socket" but my problem is little different, i need to avoid that hangup for few seconds during the write attempt.
So is there any solution ?
Or should i consider using some different mechanism for such data broadcasting for example the broadcasting loop put the data packet in some kind of FIFO buffer and client threads continuously check for available data and pick and deliver it to themselves ? This way if one thread hangs it will not stop/delay the over all distribution thread.
Any ideas please ? Thanks for your time and help.
Regards
Jams

There are no write timeouts implemented in Indy. For that, you will have to use the TIdSocketHandle.SetSockOpt() method to set the socket-level timeouts directly.
The FIFO buffer is a better option (and a better design in general). For example:
void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
...
AContext->Data = new TIdThreadSafeStringList;
...
}
void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
...
delete AContext->Data;
AContext->Data = NULL;
...
}
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
TIdThreadSafeStringList *Queue = (TIdThreadSafeStringList*) AContext->Data;
TStringList *Outbound = NULL;
TStringList *List = Queue->Lock();
try
{
if( List->Count > 0 )
{
Outbound = new TStringList;
Outbound->Assign(List);
List->Clear();
}
}
__finally
{
Queue->Unlock();
}
if( Outbound )
{
try
{
AContext->Connection->IOHandler->Write(Outbound);
}
__finally
{
delete Outbound;
}
}
...
}
...
try
{
EnterCriticalSection(&SlotListenersCriticalSection);
int i = 0;
while( i < SlotListeners->Count )
{
try
{
TIdContext *Ctx = (TIdContext*) SlotListeners->Objects[i];
TIdThreadSafeStringList *Queue = (TIdThreadSafeStringList*) Ctx->Data;
Queue->Add("Some DATA");
++i;
}
catch(const Exception &e)
{
SlotListeners->Delete(i);
}
}
}
__finally
{
LeaveCriticalSection(&SlotListenersCriticalSection);
}

Related

Dart TCP socket concatenates all 'write' sync calls as a single packet

I'm trying to send multiple packets at once to a server, but the socket keeps "merging" all sync calls to write as a single call, I did a minimal reproducible example:
import 'dart:io';
void main() async {
// <Server-side> Create server in the local network at port <any available port>.
final ServerSocket server =
await ServerSocket.bind(InternetAddress.anyIPv4, 0);
server.listen((Socket client) {
int i = 1;
client.map(String.fromCharCodes).listen((String message) {
print('Got a new message (${i++}): $message');
});
});
// <Client-side> Connects to the server.
final Socket socket = await Socket.connect('localhost', server.port);
socket.write('Hi World');
socket.write('Hello World');
}
The result is:
> dart example.dart
> Got a new message (1): Hi WorldHello World
What I expect is:
> dart example.dart
> Got a new message (1): Hi World
> Got a new message (2): Hello World
Unfortunately dart.dev doesn't support dart:io library, so you need to run in your machine to see it working.
But in summary:
It creates a new tcp server at a random port.
Then creates a socket that connects to the previous created server.
The socket makes 2 synchronous calls to the write method.
The server only receives 1 call, which is the 2 messages concatenated.
Do we have some way to receive each synchronous write call in the server as separated packets instead buffering all sync calls into a single packet?
What I've already tried:
Using socket.setOption(SocketOption.tcpNoDelay, true); right after Socket.connect instantiation, this does modify the result:
final Socket socket = await Socket.connect('localhost', server.port);
socket.setOption(SocketOption.tcpNoDelay, true);
// ...
Using socket.add('Hi World'.codeUnits); instead of socket.write(...), also does not modify the result as expected, because write(...) seems to be just a short version add(...):
socket.add('Hi World'.codeUnits);
socket.add('Hello World'.codeUnits);
Side note:
Adding an async delay to avoid calling write synchronously:
socket.add('Hi World'.codeUnits);
await Future<void>.delayed(const Duration(milliseconds: 100));
socket.add('Hello World'.codeUnits);
make it works, but I am pretty sure this is not the right solution, and this isn't what I wanted.
Environment:
Dart SDK version: 2.18.4 (stable) (Tue Nov 1 15:15:07 2022 +0000) on "windows_x64"
This is a Dart-only environment, there is no Flutter attached to the workspace.
As Jeremy said:
Programmers coding directly to the TCP API have to implement this logic themselves (e.g. by prepending a fixed-length message-byte-count field to each of their application-level messages, and adding logic to the receiving program to parse these byte-count fields, read in that many additional bytes, and then present those bytes together to the next level of logic).
So I chose to:
Prefix each message with a - and suffix with ..
Use base64 to encode the real message to avoid conflict between the message and the previously defined separators.
And using this approach, I got this implementation:
// Send packets:
socket.write('-${base64Encode("Hi World".codeUnits)}.');
socket.write('-${base64Encode("Hello World".codeUnits)}.');
And to parse the packets:
// Cache the previous parsed packet data.
String parsed = '';
void _handleCompletePacket(String rawPacket) {
// Decode the original message from base64 using [base64Decode].
// And convert the [List<int>] to [String].
final String message = String.fromCharCodes(base64Decode(rawPacket));
print(message);
}
void _handleServerPacket(List<int> rawPacket) {
final String packet = String.fromCharCodes(rawPacket);
final String next = parsed + packet;
final List<String> items = <String>[];
final List<String> tokens = next.split('');
for (int i = 0; i < tokens.length; i++) {
final String char = tokens[i];
if (char == '-') {
if (items.isNotEmpty) {
// malformatted packet.
items.clear();
continue;
}
items.add('');
continue;
} else if (char == '.') {
if (items.isEmpty) {
// malformatted packet.
items.clear();
continue;
}
_handleCompletePacket(items.removeLast());
continue;
} else {
if (items.isEmpty) {
// malformatted packet.
items.clear();
continue;
}
items.last = items.last + char;
continue;
}
}
if (items.isNotEmpty) {
// the last data of this packet was left incomplete.
// cache it to complete with the next packet.
parsed = items.last;
}
}
client.listen(_handleServerPacket);
There are certainly more optimized solutions/approaches, but I got this just for chatting messages within [100-500] characters, so that's fine for now.

Flush data added to websocket

I'm writing a speed test, but i'm having trouble on the client side for uploading.
I have a the following setup, which basically continues to write data into the socket while a condition is true, and then closes the socket:
var ws = await createWebSocket(sb.serverAddress, sb.authToken);
while (condition) {
var bytes = generateRandomBytes(_BUFFER_SIZE_BYTES);
ws.add(bytes);
print('added');
var megabits = (bytes.length * 8) / 1000000;
channel.sink.add(megabits);
}
await ws.close();
My problem is that I can't work out how to wait for the bytes to be accepted by the underlying buffer. Even if I set _BUFFER_SIZE_BYTES to an huge size it still loops at break neck speed printing out added, where I really want to wait until all the bytes are accepted by the send buffer (having been accepted by the server) before adding a new list of bytes.
With an http post request you can do: await postReq.flush();, but I don't see any such method for web sockets.
Ok so I think I have a reasonable solution to this problem.
Client side has to wait for a response from the server before sending more bytes:
var bytes = generateRandomBytes(_CHUNK_SIZE_BYTES);
ws.listen((data) async {
ws.add(bytes);
var megabits = (bytes.length * 8) / 1000000;
channel.sink.add(megabits);
}
});
Server (Go) sends a message to the client signalling that it can send a chunk, and then reads the entire response from the client, before signalling to the client that it is ready to accept another one:
for start := time.Now(); time.Since(start) < time.Second*maxDuration; {
err := conn.WriteMessage(websocket.TextMessage, []byte("next"))
if err != nil {
break
}
// will get an error if try writing to closed socket
_, bytes, err := conn.ReadMessage()
if err != nil {
fmt.Println(err)
break
}
fmt.Println(len(bytes))
}
I think this solution is ok. I've set the chunk size to 10Mb which seems to work ok. Let me know if anyone has a better idea.

lwIP mqtt connection error on stm32f4 discovery

I am trying to use lwIP for a client, which sends data to mosquitto broker on stm32f407 discovery.
Mqtt application is implemented at lwIP. I just use them like that at main after initializing.
mqtt_client_t static_client;
Afterwards, with USART interrupt, I call
example_do_connect(&static_client); example_publish(&static_client,0);
Which calls those functions:
{
struct mqtt_connect_client_info_t ci;
err_t err;
/* Setup an empty client info structure */
memset(&ci, 0, sizeof(ci));
/* Minimal amount of information required is client identifier, so set it here */
ci.client_id = "lwip_test";
ci.client_user = NULL;
ci.client_pass = NULL;
/* Initiate client and connect to server, if this fails immediately an error code is returned
otherwise mqtt_connection_cb will be called with connection result after attempting
to establish a connection with the server.
For now MQTT version 3.1.1 is always used */
err = mqtt_client_connect(client, &serverIp, MQTT_PORT, mqtt_connection_cb, 0, &ci);
/* For now just print the result code if something goes wrong*/
if(err != ERR_OK) {
}
}
and
void example_publish(mqtt_client_t *client, void *arg)
{
const char *pub_payload= "stm32_test";
err_t err;
u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
u8_t retain = 0; /* No don't retain such crappy payload... */
err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
if(err != ERR_OK) {
// printf("Publish err: %d\n", err);
err = ERR_OK;
}
}
/* Called when publish is complete either with sucess or failure */
static void mqtt_pub_request_cb(void *arg, err_t result)
{
if(result != ERR_OK) {
// printf("Publish result: %d\n", result);
}
}
I am able to ping board, my IP adress has been assigned in main by using IP_ADDR4(&serverIp, 192,168,2,97);
I've used all needed functions like MX_LWIP_Init(), MX_LWIP_Process() and actually i am even able to implement a TCP client, which is working nice. So internet connection is well, but I guess, there is a point that i missed in mqttclient. Callbacks is also have done by Erik Anderssen's guide.
When i try to subscribe to board's IP by using mosquitto, Error: no connection could be made because the target actively refused it. If you notice some point that i have missed or have an idea, please let me know.
Any help will appreciated, thanks in advance.
I had a similar problem that the server refused the connection when QoS (quality of service) was set to 2, but the server needed it to be 0. Try changing the parameter qos in the line in the connection callback to either 0 or 1:
err = mqtt_subscribe(mqtt.client, "topic", qos, MqttApp_SubscribeRequestCallback, arg);
Same applies to the parameter qos in the publish function:
change u8_t qos = 2; to u8_t qos = 0; (or 1 - whatever your server requires)
Hope it helps. Cheers.

Solace Client Acknowledgement Replay

I'm putting together a Solace Point-to-point solution in C#.
In my subscriber/listener, I am using ClientAck mode to ensure messages are successfully processed before being removed from the queue.
My question (probably due to my limited experience in messaging) is regarding failed messages, e.g. if I cannot process the message and hence not send the Ack, how is the message replayed?
An example of what I have is as follows:
using (ISession session = context.CreateSession(sessionProperties, null, null))
{
ReturnCode returnCode = session.Connect();
if (returnCode == ReturnCode.SOLCLIENT_OK)
{
var endpointProps = new EndpointProperties()
{
Permission = EndpointProperties.EndpointPermission.Consume,
AccessType = EndpointProperties.EndpointAccessType.Exclusive
};
using (IQueue queue = ContextFactory.Instance.CreateQueue(queueName))
{
session.Provision(queue, endpointProps,
ProvisionFlag.IgnoreErrorIfEndpointAlreadyExists | ProvisionFlag.WaitForConfirm, null);
_flow = session.CreateFlow(new FlowProperties { AckMode = MessageAckMode.ClientAck }, queue, null, HandleMessageEvent, HandleFlowEvent);
_flow.Start();
do { WaitEventWaitHandle.WaitOne(); } while (!cancellationToken.IsCancellationRequested);
};
return Task.CompletedTask;
}
else
{
throw new Exception($"Connection failed, return code: {returnCode}");
}
}
and then handling incoming messages
void HandleMessageEvent(object sender, MessageEventArgs args)
{
using (IMessage message = args.Message)
{
try
{
_handler(message.ApplicationMessageType, message.BinaryAttachment);
_flow.Ack(message.ADMessageId);
}
finally
{
WaitEventWaitHandle.Set();
}
}
}
So, if I don't Ack, message remains on queue as expected (and required), however, how (best-practice) can I re-process it without manual intervention?
After a message has been delivered to a consumer from a Solace PubSub+ queue, the message will only be resent if the client unbinds before sending an acknowledgement back. The exception to this is specific to JMS clients with the session.recover() action.
If a message needs to be re-delivered to a C# application after it has already been sent but not acknowledged, the client will need to unbind and rebind to the queue. Note that if there are other clients also bound to the queue, the message may be re-sent to those clients before your client rebinds.

Arduino WiFi shield post with header problems

I'm trying to do a post from the arduino wifi shield to my java servlet. The servlet functions with url get, and jquery post, but I can't sort the headers out in my arduino code. Any help will be greatly appreciated!
The server returns 200, but I'm not getting the payload "content" as value. I'm not exactly sure what I'm doing wrong but I'm pretty sure it's in how my headers are setup. I've spent the last two days trying to get it.
#include <SPI.h>
#include <WiFi.h>
char ssid[] = "jesussavesforjust19.95"; // your network SSID (name)
char pass[] = "********"; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
IPAddress server(192,168,10,149); // numeric IP for Google (no DNS)
WiFiClient client;
void setup() {
Serial.begin(9600);
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.println("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
sendData("0600890876");
}
void loop() {
// if there's incoming data from the net connection.
// send it out the serial port. This is for debugging
// purposes only:
if (client.available()) {
char c = client.read();
Serial.println(c);
}
//String dataString = "060088765";
// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
if(!client.connected())
{
Serial.println();
Serial.println("disconnecting.");
client.stop();
//sendData(dataString);
for(;;)
;
}
}
// this method makes a HTTP connection to the server:
void sendData(String thisData) {
// if there's a successful connection:
Serial.println("send data");
if (client.connect(server, 8080)) {
String content = "value=0600887654";
Serial.println(content);
Serial.println("connected");
client.println("POST /hos HTTP/1.1");
client.println("Host:localhost");
client.println("Connection:Keep-Alive");
client.println("Cache-Control:max-age=0");
client.println("Content-Type: application/x-www-form-urlencoded\n");
client.println("Content-Length: ");
client.println(content.length());
client.println("\n\n");
client.println(content);
}
else {
// if you couldn't make a connection:
Serial.println("form connection failed");
Serial.println();
Serial.println("disconnecting.");
client.stop();
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.println("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.println("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.println("signal strength (RSSI):");
Serial.println(rssi);
Serial.println(" dBm");
}
Perhaps, some of your "Serial.println" and "client.println" commands should be "Serial.print" and "client.print" instead. For example:
client.print("Content-Length: ");
client.println(content.length());
would avoid adding a line break between the text and the number.
This is maybe more advice on an approach than an answer.
If I was doing something like this I would not start on the Arduino. The endless compile, download, run, look at print()'s would drive me crazy. I would fully prototype the client/server interaction in whatever you have at your fingertips, preferably something with a debugger. (Java, Python, PHP, VB, whatever you know that you can slap together)
Second, I would run Wireshark on the server so that I could see exactly what was being sent and responded.
Then I would port the same interaction over to the Arduino. Again inspect with Wireshark to confirm you are getting what you expected. If you send the same bytes, you should get the same response.
Even if you choose to implement straight on Arduino, consider having Wireshark to capture the actual network traffic.
With Wireshark, you might see that the Arduino println() is not sending the correct line end for the server.
Also, there is no guarantee that last println() is actually sent. The network stack implementation is free to buffer as it sees fit. You might need a flush(). A packet trace would show this.
With a packet capture you might find that time matters. In theory TCP is a stream and you should be able to send that POST data 1 character at a time in 1 packet and everything would work. But the Arduino might be so slow executing those println()'s by the server's standards that it times out. In such case you would see the server respond before the Arduino even finished sending.

Resources