Character based serial communications are stubbornly difficult to program in theory. There are just too many little things that you don't think about when approaching the problem from a 'get the data, process the data' perspective. The possibilities are endless: What if the port stalls and returns no more data, what if there is a character missing in the expected data, etc. In many implementations these conditions will cause your code to completely lock up and go into an infinite loop. Although the SerialPort class of the .NET framework tries to present a clean interface, underneath this abstraction are still all the realities of serial communications.
You really need a reference implementation to even begin determining the quality parameters and pros and cons of your code ( if your concept even works at all ). Some things can be designed at a high level. Serial communications is not one of things. Get a machine, any machine that can meet the essential basic needs to create a WORKING proof of concept and go from there. What you probably want to aim for generally is something like the OSI protocol stack or TCP/IP stack. These stacks show how real robust MESSAGING system should be implemented. They include such essential features as error correction, retry aborted sessions, etc, etc. Although you may think these methods are overkill, you will find that implementing communications is not as simple as it seems, and the above mentioned messaging stacks have developed their complex nature in response to this challenging problem. Our own C-Sharp serial port class is presented below. It is far from perfect, but it's a start. using System; using System.IO.Ports; using System.Text; using System.Threading; namespace SerialPortSharp { public sealed class SerialPortConn : IDisposable { private readonly SerialPort serialPort; private readonly string returnToken; private readonly string hookOpen; private readonly string hookClose; private bool disposed; public SerialPortConn( string comPort = "Com1", int baud = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One, string returnToken = "> ", string hookOpen = "", string hookClose = "" ) { this.serialPort = new SerialPort(comPort, baud, parity, dataBits, stopBits) { ReadTimeout = 1000, RtsEnable = true, DtrEnable = true }; this.returnToken = returnToken; if (hookOpen == "") this.hookOpen = null; else this.hookOpen = hookOpen; if (hookClose == "") this.hookClose = null; else this.hookClose = hookClose; } public bool OpenConnection() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } try { this.serialPort.Open(); this.serialPort.DiscardInBuffer(); bool hooked = false; if (hookOpen != null) { hooked = this.Hook(); } else { hooked = true; } if (hooked) { return true; } else { return false; } } catch { return false; } } public bool CloseConnection() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } try { bool unhooked = false; if (hookClose != null) { unhooked = this.UnHook(); } else { unhooked = true; } if (unhooked) { Thread.Sleep(100); this.serialPort.ReadLine(); this.serialPort.DiscardInBuffer(); this.serialPort.Close(); this.serialPort.Dispose(); return true; } else { return false; } } catch { return false; } } public void Dispose() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot dispose of a disposed object."); } var closed = this.CloseConnection(); if (closed) { this.disposed = true; } else { throw new Exception("Error! Could not close port!"); } } private bool Hook() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } try { this.serialPort.Write(hookOpen + "\r"); Thread.Sleep(100); this.serialPort.DiscardInBuffer(); return true; } catch { return false; } } private bool UnHook() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } try { this.serialPort.Write(hookClose + "\r"); Thread.Sleep(100); this.serialPort.DiscardInBuffer(); return true; } catch { return false; } } private string HookTest(string serialCommand) { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } try { this.serialPort.Write(serialCommand + "\r"); Thread.Sleep(100); bool loop = true; string output = ""; while (loop) { output += this.serialPort.ReadExisting(); if (output.EndsWith(this.returnToken)) { break; } } return output; } catch (TimeoutException e) { throw new Exception("Connection failed. Read timed out."); } } public string WriteConnection(string serialCommand, bool isSafe = false) { if (this.disposed) { throw new ObjectDisposedException(this.GetType().Name, "Cannot use a disposed object."); } if (isSafe) { var output = this.HookTest(serialCommand); return output; } else { try { this.serialPort.Write(serialCommand + "\r"); Thread.Sleep(100); return this.serialPort.ReadExisting(); } catch (Exception e) { throw new Exception("Connection failed. Timed out."); } } } } }
0 Comments
I wrote a JavaScript function for validating bank account numbers in IBAN format for one of my big banking customers. This is the first version of the code. After a complete code review, many changes were suggested. Here's some of the code that came out of this exercise.
function isIbanValid(value) { var lengthMap = getLengthMap(); //cleanup value = value.toString().toUpperCase().replace(/\s/g, '').replace(/[-]/g, ''); //check if alphanumeric if (!/^[a-zA-Z0-9]+$/.test(value)) return false; //extract countrycode var countryCode = value.substring(0, 2); //check if letter if (!/([a-z]+[\s\-]?)*/i.test(countryCode)) return false; //check string length if (value.length != lengthMap[countryCode]) return false; value = value.concat(value.substring(0, 4)).substring(4); value = value.replace(countryCode, countryCodeToStringValue(countryCode)); return modulo(value, 97) == 1; function countryCodeToStringValue(countryCode) { return "".concat(((countryCode.charCodeAt(0)) - 55).toString() + (countryCode.charCodeAt(1) - 55).toString()); } function modulo(divident, divisor) { var quantization = 12; while (quantization < divident.length) { var part = divident.substring(0, quantization); divident = (part % divisor) + divident.substring(quantization); } return divident % divisor; } function getLengthMap() { var lengthMap = {}; lengthMap["AD"] = 24; lengthMap["AT"] = 20; lengthMap["AZ"] = 28; lengthMap["BH"] = 22; lengthMap["BE"] = 16; lengthMap["BA"] = 20; lengthMap["BR"] = 29; lengthMap["BG"] = 22; lengthMap["CR"] = 21; lengthMap["HR"] = 21; lengthMap["CY"] = 28; lengthMap["CZ"] = 24; lengthMap["DK"] = 18; lengthMap["DO"] = 28; lengthMap["EE"] = 20; lengthMap["FO"] = 18; lengthMap["FI"] = 18; lengthMap["FR"] = 27; lengthMap["DE"] = 22; lengthMap["GR"] = 27; lengthMap["GI"] = 23; lengthMap["GL"] = 18; lengthMap["GT"] = 28; lengthMap["HU"] = 28; lengthMap["IS"] = 26; lengthMap["IE"] = 22; lengthMap["IL"] = 23; lengthMap["IT"] = 27; lengthMap["JO"] = 30; lengthMap["KZ"] = 20; lengthMap["KW"] = 30; lengthMap["LV"] = 21; lengthMap["LB"] = 28; lengthMap["LI"] = 21; lengthMap["LT"] = 20; lengthMap["LU"] = 20; lengthMap["MK"] = 19; lengthMap["MT"] = 31; lengthMap["MR"] = 27; lengthMap["MU"] = 30; lengthMap["MC"] = 27; lengthMap["MD"] = 24; lengthMap["ME"] = 22; lengthMap["NL"] = 18; lengthMap["NO"] = 15; lengthMap["PK"] = 24; lengthMap["PS"] = 29; lengthMap["PL"] = 28; lengthMap["PT"] = 25; lengthMap["QA"] = 29; lengthMap["RO"] = 24; lengthMap["SM"] = 27; lengthMap["SA"] = 24; lengthMap["RS"] = 22; lengthMap["SK"] = 24; lengthMap["SI"] = 19; lengthMap["ES"] = 24; lengthMap["SE"] = 24; lengthMap["CH"] = 21; lengthMap["TN"] = 24; lengthMap["TR"] = 26; lengthMap["AE"] = 23; lengthMap["GB"] = 22; return lengthMap; } }
Someone suggested rewriting the whole thing as this:
var validIBAN = (function () { // use an IIFE // A "constant" lookup table of IBAN lengths per country // (the funky formatting is just to make it fit better in the answer here on CR) var CODE_LENGTHS = { AD: 24, AE: 23, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22, BR: 29, CH: 21, CR: 21, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20, ES: 24, FI: 18, FO: 18, FR: 27, GB: 22, GI: 23, GL: 18, GR: 27, GT: 28, HR: 21, HU: 28, IE: 22, IL: 23, IS: 26, IT: 27, JO: 30, KW: 30, KZ: 20, LB: 28, LI: 21, LT: 20, LU: 20, LV: 21, MC: 27, MD: 24, ME: 22, MK: 19, MR: 27, MT: 31, MU: 30, NL: 18, NO: 15, PK: 24, PL: 28, PS: 29, PT: 25, QA: 29, RO: 24, RS: 22, SA: 24, SE: 24, SI: 19, SK: 24, SM: 27, TN: 24, TR: 26 }; // piece-wise mod97 using 9 digit "chunks", as per Wikipedia's example: // http://en.wikipedia.org/wiki/International_Bank_Account_Number#Modulo_operation_on_IBAN function mod97(string) { var checksum = string.slice(0, 2), fragment; for(var offset = 2 ; offset < string.length ; offset += 7) { fragment = String(checksum) + string.substring(offset, offset + 7); checksum = parseInt(fragment, 10) % 97; } return checksum; } // return a function that does the actual work return function (input) { var iban = String(input).toUpperCase().replace(/[^A-Z0-9]/g, ''), // keep only alphanumeric characters code = iban.match(/^([A-Z]{2})(\d{2})([A-Z\d]+)$/), // match and capture (1) the country code, (2) the check digits, and (3) the rest digits; // check syntax and length if(!code || iban.length !== CODE_LENGTHS[code[1]]) { return false; } // rearrange country code and check digits, and convert chars to ints digits = (code[3] + code[1] + code[2]).replace(/[A-Z]/g, function (letter) { return letter.charCodeAt(0) - 55; }); // final check return mod97(digits) === 1; }; }());
Somebody else suggested making better mod97 code
static mod97(acc, digit, index) { return index == 9 || (index > 9 && index % 7 == 0) ? (Number(acc + digit) % 97).toString() : acc + digit } Requirements
Here is a two part C program that is used to form a client / server application. It is just an example of how to handle multiple clients connecting to a server. Some clients will crash and die, but the server must handle them and cleanup afterward.
The Server#include <stdio.h> #include <string.h> //strlen #include <stdlib.h> //strlen #include <sys/socket.h> #include <arpa/inet.h> //inet_addr #include <unistd.h> //write #include <pthread.h> //for threading , link with lpthread //the thread function void *connection_handler(void *); int main(int argc , char *argv[]) { int socket_desc , client_sock , c , *new_sock; struct sockaddr_in server , client; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) { printf("Could not create socket"); } puts("Socket created"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) { //print the error message perror("bind failed. Error"); return 1; } puts("bind done"); //Listen listen(socket_desc , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); c = sizeof(struct sockaddr_in); //Accept and incoming connection /*puts("Waiting for incoming connections..."); c = sizeof(struct sockaddr_in);*/ while( (client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c)) ) { puts("Connection accepted"); pthread_t sniffer_thread; new_sock = malloc(sizeof(new_sock *)); *new_sock = client_sock; if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) new_sock) < 0) { perror("could not create thread"); return 1; } //Now join the thread , so that we dont terminate before the thread //pthread_join( sniffer_thread , NULL); // was commented before puts("Handler assigned"); } if (client_sock < 0) { perror("accept failed"); return 1; } return 0; } /* * This will handle connection for each client * */ void *connection_handler(void *socket_desc) { //Get the socket descriptor int sock = *(int*)socket_desc; int read_size; char *message , client_message[2000]; //Receive a message from client while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 ) { //Send the message back to client write(sock , client_message , strlen(client_message)); printf("%s\n",client_message); } if(read_size == 0) { puts("Client disconnected"); fflush(stdout); } else if(read_size == -1) { perror("recv failed"); } //Free the socket pointer free(socket_desc); close(sock); pthread_exit(NULL); return; } The Client
#include<stdio.h> //printf #include<string.h> //strlen #include<sys/socket.h> //socket #include<arpa/inet.h> //inet_addr int main(int argc , char *argv[]) { int sock; struct sockaddr_in server; char message[1000] , server_reply[2000]; //Create socket sock = socket(AF_INET , SOCK_STREAM , 0); if (sock == -1) { printf("Could not create socket"); } puts("Socket created"); server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_family = AF_INET; server.sin_port = htons( 8888 ); //Connect to remote server if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) { perror("connect failed. Error"); return 1; } puts("Connected\n"); //keep communicating with server while(1) { printf( "Type 'q' to end the session:\n"); printf("Now type your message:"); scanf("%s" , message); if( message[0] == 'q') { close(sock); return 0; } else{ //Send some data if( send(sock , message , strlen(message) , 0) < 0) { puts("Send failed"); return 1; } //Receive a reply from the server if( recv(sock , server_reply , 2000 , 0) < 0) { puts("recv failed"); break; } puts("Server reply : "); puts(server_reply); } } close(sock); return 0; }
Source: https://codereview.stackexchange.com/questions/143286/multithreaded-client-server-communication
Somebody once said that we needed to memset the buffer. So, here's their suggestion for the server code:
//Receive a message from client while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 ) { //Send the message back to client write(sock , client_message , strlen(client_message)); printf("%s\n",client_message); } |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories
All
|