123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- MODULE UDPChatServer; (** AUTHOR "SAGE"; PURPOSE "UDP Chat Server" *)
- IMPORT
- Base := UDPChatBase, UDP, IP,
- Dates, Strings, Modules, Kernel, Events;
- CONST
- branchInit = 0;
- branchPacketReceive = 1;
- branchVersionCheck = 2;
- branchAuthentication = 3;
- branchPacketHandle = 4;
- branchEnd = 5;
- branchTerminated = 6;
- moduleName = "UDPChatServer";
- (* Event classification as in Events.XML *)
- EventClass = 3; (* UDP Chat *)
- EventSubclass = 3; (* UDP Chat Server *)
- TYPE
- String = Strings.String;
- Instance = OBJECT
- VAR
- s: UDP.Socket;
- dt: Dates.DateTime;
- running, terminated: BOOLEAN;
- ip: IP.Adr;
- branch, command, seqNum, messageType: INTEGER;
- uin, receiverUin, port, len, receiveBufOffset: LONGINT; res: WORD;
- user: Base.User;
- users: Base.Users;
- clients: Base.List;
- client, receiver: Base.Client;
- sendBuf: Base.Buffer;
- receiveBuf, password, shortName, fullName, eMail, message, textCode: String;
- str1, str2: ARRAY 256 OF CHAR;
- ACKReq: Base.ACKRec;
- PROCEDURE &New *(udp: UDP.Socket);
- BEGIN
- s := udp
- END New;
- PROCEDURE Destroy;
- BEGIN
- running := FALSE;
- s.Close;
- BEGIN {EXCLUSIVE}
- AWAIT (terminated)
- END;
- END Destroy;
- PROCEDURE FinalizeClients(clients: Base.List);
- VAR
- i: LONGINT;
- p: ANY;
- client: Base.Client;
- BEGIN
- i := 0;
- WHILE i < clients.GetCount () DO
- p := clients.GetItem (i);
- client := p (Base.Client);
- client.Finalize;
- INC (i);
- END;
- END FinalizeClients;
- PROCEDURE FindClient (clients: Base.List;
- uin: LONGINT;
- VAR client: Base.Client): BOOLEAN;
- VAR
- i: LONGINT;
- p: ANY;
- BEGIN
- i := 0;
- WHILE i < clients.GetCount () DO
- p := clients.GetItem (i);
- client := p (Base.Client);
- IF uin = client.uin THEN
- RETURN TRUE;
- END;
- INC (i);
- END;
- RETURN FALSE;
- END FindClient;
- PROCEDURE CheckKeepAlive (clients: Base.List);
- VAR
- i: LONGINT;
- p: ANY;
- BEGIN
- i := 0;
- WHILE i < clients.GetCount () DO
- p := clients.GetItem (i);
- client := p (Base.Client);
- IF Kernel.Expired (client.keepAliveTimer) THEN
- MulticastStatus (clients, client, Base.USER_OFFLINE, sendBuf, s);
- client.Finalize;
- clients.Remove (client);
- END;
- INC (i);
- END;
- END CheckKeepAlive;
- PROCEDURE Server_NewUserReply (ip: IP.Adr; port: LONGINT; uin: LONGINT;
- seqNum: INTEGER; sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- res: WORD;
- string: String;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (Base.NEW_USER_REPLY, seqNum, sendBuf);
- sendBuf.AddInt (uin, 4);
- string := sendBuf.GetString ();
- s.Send (ip, port, string^, 0, sendBuf.GetLength (), res);
- END Server_NewUserReply;
- PROCEDURE Server_LoginReply (client: Base.Client;
- sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- res: WORD;
- string: String;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (Base.LOGIN_REPLY, client.inSeqNum, sendBuf);
- sendBuf.AddInt (client.uin, 4);
- string := sendBuf.GetString ();
- s.Send (client.ip, client.port, string^, 0, sendBuf.GetLength (), res);
- END Server_LoginReply;
- PROCEDURE Server_InfoReply (client: Base.Client;
- user: Base.User; sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- string: String;
- res: WORD; len: LONGINT;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (Base.INFO_REPLY, client.inSeqNum, sendBuf);
- sendBuf.AddInt (user.uin, 4);
- len := Strings.Length (user.shortName) + 1;
- sendBuf.AddInt (len, 2);
- sendBuf.Add (user.shortName, 0, len, TRUE, res);
- string := sendBuf.GetString ();
- s.Send (client.ip, client.port, string^, 0, sendBuf.GetLength (), res);
- END Server_InfoReply;
- PROCEDURE Server_ACK (client: Base.Client;
- sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- res: WORD;
- string: String;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (Base.ACK, client.inSeqNum, sendBuf);
- string := sendBuf.GetString ();
- s.Send (client.ip, client.port, string^, 0, sendBuf.GetLength (), res);
- END Server_ACK;
- PROCEDURE Server_UserStatus (client, receiver: Base.Client;
- status: INTEGER; sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- res: WORD;
- string: String;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (status, receiver.outSeqNum, sendBuf);
- NEW (ACKReq);
- ACKReq.seqNum := receiver.outSeqNum;
- receiver.ACKList.Add (ACKReq);
- INC (receiver.outSeqNum);
- sendBuf.AddInt (client.uin, 4);
- string := sendBuf.GetString ();
- s.Send (receiver.ip, receiver.port, string^, 0, sendBuf.GetLength (), res);
- END Server_UserStatus;
- PROCEDURE Server_ReceiveMessage (client, receiver: Base.Client; dt: Dates.DateTime;
- messageType: INTEGER; message: String; sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- string: String;
- res: WORD; len: LONGINT;
- BEGIN {EXCLUSIVE}
- Base.ServerPacketInit (Base.RECEIVE_MESSAGE, receiver.outSeqNum, sendBuf);
- NEW (ACKReq);
- ACKReq.seqNum := receiver.outSeqNum;
- receiver.ACKList.Add (ACKReq);
- INC (receiver.outSeqNum);
- sendBuf.AddInt (client.uin, 4);
- sendBuf.AddInt (dt.year, 2);
- sendBuf.AddInt (dt.month, 1);
- sendBuf.AddInt (dt.day, 1);
- sendBuf.AddInt (dt.hour, 1);
- sendBuf.AddInt (dt.minute, 1);
- sendBuf.AddInt (messageType, 2);
- (*
- len := Strings.Length (message^) + 1;
- *)
- len := LEN (message^);
- sendBuf.AddInt (len, 2);
- sendBuf.Add (message^, 0, len, TRUE, res);
- string := sendBuf.GetString ();
- s.Send (receiver.ip, receiver.port, string^, 0, sendBuf.GetLength (), res);
- END Server_ReceiveMessage;
- PROCEDURE MulticastStatus (clients: Base.List;
- client: Base.Client;
- status: INTEGER; sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- i: LONGINT;
- p: ANY;
- receiver: Base.Client;
- BEGIN
- i := 0;
- WHILE i < clients.GetCount () DO
- p := clients.GetItem (i);
- receiver := p (Base.Client);
- IF client.uin # receiver.uin THEN
- Server_UserStatus (client, receiver, status, sendBuf, s);
- IF status = Base.USER_ONLINE THEN
- Server_UserStatus (receiver, client, status, sendBuf, s);
- END;
- END;
- INC (i);
- END;
- END MulticastStatus;
- PROCEDURE MulticastMessage (clients: Base.List;
- client: Base.Client; dt: Dates.DateTime; messageType: INTEGER; message: String;
- sendBuf: Base.Buffer; s: UDP.Socket);
- VAR
- i: LONGINT;
- p: ANY;
- receiver: Base.Client;
- BEGIN
- i := 0;
- WHILE i < clients.GetCount () DO
- p := clients.GetItem (i);
- receiver := p (Base.Client);
- (*IF client.uin # receiver.uin THEN*)
- Server_ReceiveMessage (client, receiver, dt, messageType, message, sendBuf, s);
- (*END;*)
- INC (i);
- END;
- END MulticastMessage;
- BEGIN {ACTIVE}
- branch := branchInit;
- REPEAT
- CASE branch OF
- | branchInit:
- NEW (receiveBuf, Base.MaxUDPDataLen);
- NEW (sendBuf, 0);
- NEW (clients);
- NEW (users);
- running := TRUE;
- terminated := FALSE;
- branch := branchPacketReceive;
- | branchPacketReceive:
- IF running THEN
- s.Receive (receiveBuf^, 0, Base.MaxUDPDataLen, 1, ip, port, len, res);
- IF (res = UDP.Ok) & (len > 0) THEN
- receiveBufOffset := 0;
- branch := branchVersionCheck;
- END;
- CheckKeepAlive (clients);
- ELSE
- branch := branchEnd;
- END;
- | branchVersionCheck:
- IF Base.BufGetInt (receiveBuf, receiveBufOffset) = Base.VERSION THEN
- branch := branchAuthentication;
- ELSE
- branch := branchPacketReceive;
- END;
- | branchAuthentication:
- command := Base.BufGetInt (receiveBuf, receiveBufOffset);
- seqNum := Base.BufGetInt (receiveBuf, receiveBufOffset);
- uin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
- Strings.IntToStr (seqNum, str1);
- Strings.Concat (" SeqNum: ", str1, str1);
- Strings.Concat (str1, " Command: ", str1);
- Strings.IntToStr (uin, str2);
- Strings.Concat ("User ID: ", str2, str2);
- Strings.Concat (str2, str1, str1);
- Base.CommandDecode (command, str2);
- Strings.Concat (str1, str2, str1);
- Log (Events.Information, 0, str1, FALSE);
- IF FindClient (clients, uin, client) THEN
- (* Additional check *)
- IF (IP.AdrsEqual (client.ip, ip)) & (client.port = port) THEN
- branch := branchPacketHandle;
- ELSE
- branch := branchPacketReceive;
- END;
- ELSE
- CASE command OF
- | Base.LOGIN:
- password := Base.BufGetString (receiveBuf, receiveBufOffset);
- IF users.PasswordCorrect (uin, password) THEN
- NEW (client);
- client.ip := ip;
- client.port := port;
- client.uin := uin;
- client.inSeqNum := seqNum;
- client.outSeqNum := 0;
- Kernel.SetTimer (client.keepAliveTimer, Base.clientKeepAliveAwait);
- clients.Add (client);
- Server_LoginReply (client, sendBuf, s);
- (* Now we will send client status to all other On-Line clients,
- and they statuses to this client *)
- MulticastStatus (clients, client, Base.USER_ONLINE, sendBuf, s);
- END;
- | Base.NEW_USER_REG:
- password := Base.BufGetString (receiveBuf, receiveBufOffset);
- shortName := Base.BufGetString (receiveBuf, receiveBufOffset);
- fullName := Base.BufGetString (receiveBuf, receiveBufOffset);
- eMail := Base.BufGetString (receiveBuf, receiveBufOffset);
- user := users.Add (password, shortName, fullName, eMail);
- Server_NewUserReply (ip, port, user.uin, seqNum, sendBuf, s);
- ELSE
- END;
- branch := branchPacketReceive;
- END;
- | branchPacketHandle:
- IF command = Base.ACK THEN
- IF Base.SeqNumInACKList (client.ACKList, seqNum, ACKReq) THEN
- client.ACKList.Remove (ACKReq);
- END;
- ELSIF Base.isNextSeqNum (seqNum, client.inSeqNum) THEN
- client.inSeqNum := seqNum;
- CASE command OF
- | Base.SEND_MESSAGE:
- Server_ACK (client, sendBuf, s);
- receiverUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
- messageType := Base.BufGetInt (receiveBuf, receiveBufOffset);
- message := Base.BufGetString (receiveBuf, receiveBufOffset);
- dt := Dates.Now ();
- IF receiverUin = 0 THEN
- MulticastMessage (clients, client, dt, messageType, message, sendBuf, s);
- ELSE
- IF FindClient (clients, receiverUin, receiver) THEN
- Server_ReceiveMessage (client, receiver, dt, messageType, message, sendBuf, s);
- ELSE
- (*
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- *)
- END;
- END;
- | Base.KEEP_ALIVE:
- Server_ACK (client, sendBuf, s);
- Kernel.SetTimer (client.keepAliveTimer, Base.clientKeepAliveAwait);
- | Base.INFO_REQ:
- receiverUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
- user := users.Find (receiverUin);
- IF user # NIL THEN
- Server_InfoReply (client, user, sendBuf, s);
- END;
- | Base.SEND_TEXT_CODE:
- Server_ACK (client, sendBuf, s);
- textCode := Base.BufGetString (receiveBuf, receiveBufOffset);
- IF textCode^ = "USER_DISCONNECTED" THEN
- MulticastStatus (clients, client, Base.USER_OFFLINE, sendBuf, s);
- clients.Remove (client);
- ELSE
- (*
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- *)
- END;
- ELSE
- END;
- END;
- branch := branchPacketReceive;
- | branchEnd:
- users.Store;
- FinalizeClients (clients);
- clients.Clear;
- BEGIN {EXCLUSIVE}
- terminated := TRUE
- END;
- branch := branchTerminated;
- ELSE
- END;
- UNTIL branch = branchTerminated;
- END Instance;
- VAR
- instance: Instance;
- PROCEDURE Log (type, code : SHORTINT; msg: ARRAY OF CHAR; showOnKernelLog : BOOLEAN);
- VAR message : Events.Message;
- BEGIN
- COPY(msg, message);
- Events.AddEvent(moduleName, type, EventClass, EventSubclass, code, message, showOnKernelLog);
- END Log;
- PROCEDURE Start* ;
- VAR
- s: UDP.Socket;
- res: WORD;
- str: ARRAY 256 OF CHAR;
- BEGIN
- IF instance = NIL THEN
- NEW (s, Base.serverPort, res);
- IF res = UDP.Ok THEN
- NEW (instance, s);
- Strings.IntToStr (Base.serverPort, str);
- Strings.Concat ("server started on port: ", str, str);
- Log (Events.Information, 0, str, TRUE);
- ELSE
- Log (Events.Error, 0, "server NOT started!", TRUE);
- END;
- END;
- END Start;
- PROCEDURE Stop*;
- BEGIN
- Cleanup;
- END Stop;
- (** Termination handler. *)
- PROCEDURE Cleanup;
- BEGIN
- IF instance # NIL THEN
- instance.Destroy;
- END;
- END Cleanup;
- BEGIN
- Modules.InstallTermHandler (Cleanup);
- END UDPChatServer.
|