TCP/IP Server
Θα υλοποιήσω ένα απλό TCP/IP πολυνηματικό διακομιστή (Server) χρησιμοποιώντας τα TServerSocket & TClientSocket components.
(Απαιτείται να εγκατασταθεί το package /bin/dclsockets70.bpl)
Στο http://delphi.about.com/od/networking/l/aa112602a.htm θα βρείτε περισσότερες πληροφορίες για την χρήση των συγκεκριμένων components.
Επίσης στο http://www.overbyte.be/frame_index.h...ducts/ics.html θα βρείτε μια διαφορετική υλοποίηση από Indy και Delphi socket components.

Για πελάτες (clients) θα χρησιμοποιήσουμε

1. Ένα απλό browser που θα «χτυπάει» μία http διεύθυνση
Κώδικας:
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
και στην περίπτωσή μας http://127.0.0.1:1024

H παραπάνω HTTP κλήση εμφανίζεται στον Server σαν το παρακάτω κείμενο
Κώδικας:
GET / HTTP/1.1
Host: 127.0.0.1:1024
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/15.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,el;q=0.8,el-gr;q=0.5,en-us;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Ο Server απαντά
Κώδικας:
HTTP/1.1 200 OK
Content-Length: 11
Hello World
Connection: close
[empty line]


2. Το telnet στο οποίο θα επιστρέφεται ότι στέλνουμε (echo).
Start/Run Cmd
Στο Dos box πληκτρολογούμε telnet 127.0.0.1 1024
Πληκτρολογούμε «Hello» και το αποστέλλουμε στον Server με Enter
Κώδικας:
Client  ---------- initate connection -----------> Server
...
Client  <---------------command------------------- Server
Client  ----------------response-----------------> Server
...
Client  <---------------command------------------- Server
Client  ----------------response-----------------> Server
Πρέπει να υλοποιηθεί το RFC 854, και δεν μπορεί να συνυπάρξει με το modbus ή το Web server


3. To modbus client.
Θα πρέπει να επιλέξουμε για θύρα που ακούει ο Server την 502.

Κώδικας:
|Modbus Application Protocol (MBAP) Header | Protocol Data Unit (PDU)|
|------------------------------------------+-------------------------|
|Transaction |Protocol  | Length  | Unit   | Function   |   Data     |
|Identifier  |Identifier| Field   | ID     | Code       |            |
|--------------------------------------------------------------------|
| (2 bytes)  |(2 bytes) |(2 bytes)|(1 byte)|(1 byte)    | Μεταβλητό  |
|------------------------------------------+-------------------------|
Υλοποίησα, στον TCP/Server, ένα modbus client που υποστηρίζει μόνο τις παρακάτω functions.

03 (0x03) Read Holding Registers
Βλέπε MODBUS Application Protocol Specification V1.1b σελίδα 19/51
http://www.modbus.org/docs/Modbus_Ap...ocol_V1_1b.pdf

06 (0x06) Write Single Register
Βλέπε. MODBUS Application Protocol Specification V1.1b σελίδα



Statefull
Ένας WebServer, δημιουργεί για κάθε νέα σύνδεση ένα SessionId που το επιστρέφει στον Client σαν Cookie. Με κλειδί αυτό το SessionId, ο WebServer, συσχετίζει όλη τα δεδομένα του συγκεκριμένου client (user name, db connection κτλ)
Ο ίδιος client, στην επόμενη επικοινωνία του, αποστέλλει το SessionId ώστε να επαναχρησιμοποιήσει τα δεδομένα που όρισε σε προηγούμενη κλήση (statefull).

Περιγραφή του προγράμματος.
Ο server ακούει στην πόρτα 1024 και για κάθε νέα σύνδεση πελάτη δημιουργεί ένα ξεχωριστό thread που θα χειρίζεται την επικοινωνία μεταξύ τους (ServerType= stThreadBlocking). Μόλις αποσταλεί η απάντηση στον πελάτη, το νήμα τελειώνει την εργασία του και επιστρέφει στο pool. Η επόμενη κλήση από τον ίδιο πελάτη δεν θα συσχετισθεί με καμία προηγούμενη (stateless).
Δημιουργούνται νήματα (threads) κλάσης που κληρονομεί από την TServerClientThread και επιστρέφονται στο Server μέσω του event OnGetThread.
Κώδικας:
procedure TFrmServer.ServerGetThread(Sender: TObject;  ClientSocket: TServerClientWinSocket;   var SocketThread: TServerClientThread);
begin
 SocketThread:=TSrvThread.Create(ClientSocket, Handle);
end;

TSrvThread=class(TServerClientThread)
           private
            fHandle:THandle;
...
           protected
            procedure ClientExecute; override; 
           public
            constructor Create(aSocket:TServerClientWinSocket;
           aHandle:THandle);
           end;
Στην procedure TSrvThread.ClientExecute; διαβάζουμε το εισερχόμενο TCP/IP μήνυμα.

Η ρουτίνα RequestType το κατηγοριοποιεί σαν rtHTTP, rtText, rtModBus ή rtBinary και απαντάει ανάλογα.

Στο http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html περιγράφεται ότι η πρώτη γραμμή ορίζεται σαν

Κώδικας:
Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
Όπου Method         = "OPTIONS"                ; Section 9.2
                      | "GET"                    ; Section 9.3
                      | "HEAD"                   ; Section 9.4
                      | "POST"                   ; Section 9.5
                      | "PUT"                    ; Section 9.6
                      | "DELETE"                 ; Section 9.7
                      | "TRACE"                  ; Section 9.8
                      | "CONNECT"                ; Section 9.9
                      | extension-method
       extension-method = token
Στο πρόγραμμα κατηγοριοποιώ την κλήση σαν http αν περιέχει το HTTP/1. και ξεκινάει με τις λέξεις GET ή HEAD.

Ο χειρισμός του telnet, είναι διαφορετικός. To socket παραμένει «ανοικτό» έως να έρθει κάποιος control χαρακτήρας.

Το πρόγραμμα χρειάζεται επεκτάσεις εάν κάποιος θέλει να κάνει πραγματική δουλειά με αυτό.

Η πρώτη και σημαντικότερη είναι η υλοποίηση μιας κλάσης TModbus που θα υλοποιεί το modbus πρωτόκολλο έτσι ώστε ο κώδικας (C μεταφιεσμένη σε Pascal) να γίνει συντηρήσιμος.

Κώδικας:
  
  rtModBus : begin
         case pbFC^ of
           $03 : begin // mbfReadHoldingRegs = $03;
                  pa:=pword(FRequest);
                  pd:=pa;
                  inc(pa, 4); // points to Start Address
                  inc(pd, 5); // point to count
                  move(FRequest^,data[0],8);
                  cnt:=swap(pd^);
                  data[ 8]:=cnt*2;
                  pd:=pword(@data[9]);
                  for i:=0 to pred(cnt) do begin
                    w:=SendMessage(fHandle,REG_GET,swap(pa^)+i,0);
                    pd^:=swap(w);
                    inc(pd);
                   end;
                  size:=8+1+cnt*2;
                  data[5]:=2 + 1 + cnt*2;
                  ClientSocket.SendBuf(data,size);
                 end;
Μπορείτε να κατεβάσετε την εφαρμογή και το source από το
http://www.filefactory.com/file/dftv...n/Server01_rar

Και παραφράζοντας τον μαύρο πειρατή (Αστερίξ στη Κορσική σελ. 19)
Bugus humanum est

Καλό φθινόπωρο