serverex.pp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. Program Socket_Comms_Test;
  2. {***************************************************************************
  3. TCP/IP Streaming Socket Server Example program.
  4. NumberofConnections is the number of consecutive active connections I will
  5. allow. I have not hit this limit yet.
  6. This defaults to port 5000
  7. The MOST Important thing to look at when doing socket calls of any kind
  8. is the byte order in the structure. Got caught big time with this in reference
  9. to the port number.
  10. This program runs as-is, just telnet localhost 5000 to connect to it.
  11. No warranty at all, I will not be responsible if this sets fire to your dog!
  12. This is exactly as I use it, I have just put the references to my db unit
  13. in curly brackets. It just echoes back what you type on a line by line basis
  14. Run it in X or on a seperate virtual console to the one you are telneting from
  15. as it prints a LOT of info to the console about what it is doing and such.
  16. I'm not a pretty coder at all, so please, no complaints about the lack of
  17. comments or coding style, unless they are very contructive ;p)
  18. type 'quit', minus the quotes and in lower case on the console to exit the
  19. program. The only problem I can see with this, is if you exit it, it does
  20. not shut down the connections to the telnet sessions cleanly, and therefore
  21. it leaves port 5000 in a TIME_WAIT state for a couple of minutes. This prevents
  22. you re-running the program immediately as it will not bind to the port.
  23. (Bind Error 98).
  24. If you know how to fix this, please let me know and I'll update the code.
  25. If you exit all your telnet sessions before shutting the server down, it
  26. works fine.
  27. Hope some of you find this usefull. I wrote it, purely because there is a
  28. big lack of examples of linux port use in FPC. And I know NO C, therefore
  29. the examples on the net meant nothing to me.
  30. All I ask is :-
  31. If you like it, use it or want to change it, please drop me an E-mail.
  32. Regards Brad Campbell
  33. [email protected]
  34. ***************************************************************************}
  35. {$mode ObjFPC}
  36. Uses Linux, Sockets, Sysutils{, dbu};
  37. Const
  38. NumberofConnections = 5;
  39. Type ConnectionType = Record
  40. IP : Cardinal;
  41. Port : Word;
  42. Handle : Integer;
  43. Connected : Boolean;
  44. IdleTimer : Integer;
  45. End;
  46. Var
  47. Connection : Array[1..NumberofConnections] Of ConnectionType;
  48. FDS : FDSet;
  49. S : LongInt;
  50. PortNumber : Word;
  51. GreatestHandle : Integer;
  52. Quit : Boolean;
  53. Command : String;
  54. Procedure ZeroConnection;
  55. Var Loop : Integer;
  56. Begin
  57. For Loop := 1 To NumberOfConnections Do
  58. Connection[Loop].Connected := False;
  59. End;
  60. Function FreeConnections : Integer;
  61. Var Loop : Integer;
  62. Begin
  63. Result := 0;
  64. For Loop := 1 To NumberOfConnections Do
  65. If Not Connection[Loop].Connected Then Inc(Result);
  66. FreeConnections := Result;
  67. End;
  68. Function GetFreeConnection : Integer;
  69. Var Loop : Integer;
  70. Found : Boolean;
  71. Begin
  72. Result := 0;
  73. Loop := 1;
  74. Found := False;
  75. While (Loop < NumberOfConnections + 1) and (Not Found) Do
  76. Begin
  77. If Not Connection[Loop].Connected Then
  78. Begin
  79. Found := True;
  80. Result := Loop;
  81. End;
  82. Inc(Loop);
  83. GetFreeConnection := Result;
  84. End;
  85. End;
  86. Procedure PError(S : String);
  87. Begin
  88. Writeln(S,SocketError);
  89. Halt(100);
  90. End;
  91. Procedure PDebug(S : String);
  92. Begin
  93. Writeln(S);
  94. End;
  95. Procedure PDebugNOLF(S: String);
  96. Begin
  97. Write(S);
  98. End;
  99. Function SockAddrtoString(InAddr : LongWord) : String;
  100. Var
  101. P1,P2,P3,P4 : Byte;
  102. S1,S2,S3,S4 : String;
  103. Begin
  104. P1 := (InAddr And $ff000000) Shr 24;
  105. P2 := (InAddr And $ff0000) Shr 16;
  106. P3 := (InAddr And $ff00) Shr 8;
  107. P4 := InAddr And $FF;
  108. Str(P1,S1);
  109. Str(P2,S2);
  110. Str(P3,S3);
  111. Str(P4,S4);
  112. SockAddrtoString := S4+'.'+S3+'.'+S2+'.'+S1;
  113. End;
  114. Procedure WelcomeHandle(Handle, ConnNum : Integer);
  115. Var Buffer : String;
  116. Sent : Integer;
  117. Begin
  118. Buffer := 'Welcome to Brads Server 1.0'+#10+#13+'You Are Connection '+
  119. InttoStr(ConnNum)+' Of '+InttoStr(NumberofConnections)+
  120. ', With '+InttoStr(FreeConnections)+' Connections Free'#13+#10;
  121. Sent := Send(Handle,Buffer[1],Length(Buffer),0);
  122. If Sent <> Length(Buffer) Then
  123. PDebug('Wanted to Send : ' +InttoStr(Length(Buffer))+' Sent Only : '
  124. +InttoStr(Sent)+' to Connection : '+InttoStr(ConnNum));
  125. End;
  126. Procedure AcceptNewConnection;
  127. Var ConnectionNumber : Integer;
  128. Handle : LongInt;
  129. FromAddrSize : LongInt;
  130. FromAddr : TInetSockAddr;
  131. Begin
  132. FromAddrSize := Sizeof(FromAddr);
  133. If FreeConnections > 0 Then
  134. Begin
  135. ConnectionNumber := GetFreeConnection;
  136. PDebug('Accepting New Connection Number : '+InttoStr(ConnectionNumber));
  137. Handle := Accept(S,FromAddr,FromAddrSize);
  138. If Handle < 0 Then PError('Accept Error!!');
  139. PDebug('Accepted From : '+SockAddrtoString(FromAddr.Addr)+' Port : '
  140. +Inttostr(Swap(FromAddr.Port)));
  141. Connection[ConnectionNumber].Handle := Handle;
  142. Connection[ConnectionNumber].IP := FromAddr.Addr;
  143. Connection[ConnectionNumber].Port := FromAddr.Port;
  144. Connection[ConnectionNumber].Connected := True;
  145. Connection[ConnectionNumber].IdleTimer := 0;
  146. WelcomeHandle(Handle,ConnectionNumber);
  147. End;
  148. End;
  149. Procedure SetUpSocket;
  150. Var
  151. SockAddr : TInetSockAddr;
  152. yes : longint;
  153. Begin
  154. SockAddr.Family := AF_INET;
  155. SockAddr.Port := Swap(PortNumber);
  156. SockAddr.Addr := 0;
  157. S := Socket(AF_INET,SOCK_STREAM,0);
  158. If SocketError <> 0 Then PError('Socket Error : ');
  159. yes := $1010101; {Copied this from existing code. Value is empiric,
  160. but works. (yes=true<>0) }
  161. SetSocketOptions(s, SOL_SOCKET, SO_REUSEADDR,yes,sizeof(yes));
  162. If Not Bind(S,SockAddr,SizeOf(SockAddr)) Then PError('Bind Error : ');
  163. If Not Listen(S,5) Then PError('Listen Error : ');
  164. End;
  165. Procedure LoadConnectedFDS;
  166. Var Loop : Integer;
  167. Begin
  168. For Loop := 1 To NumberOfConnections Do
  169. If Connection[Loop].Connected Then
  170. Begin
  171. FD_SET(Connection[Loop].Handle,FDS);
  172. If Connection[Loop].Handle > GreatestHandle Then
  173. GreatestHandle := Connection[Loop].Handle;
  174. End;
  175. End;
  176. Procedure ServiceHandle(Handle, ConnectionNum : Integer);
  177. Var Buffer : String;
  178. Sent, BufferLength : Integer;
  179. Begin
  180. Writeln('Service Handle : ',Handle);
  181. BufferLength := Recv(Handle,Buffer[1],200,0);
  182. Setlength(Buffer,BufferLength);
  183. If SocketError <> 0 Then
  184. PDebug('Reciceved Socket Error : '
  185. +InttoStr(SocketError)+' OnHandle '+InttoStr(Handle));
  186. If BufferLength = 0 Then {It's EOF, Socket has been closed}
  187. Begin
  188. PDebug('Socket Handle '+InttoStr(Handle)+' Closed');
  189. Connection[ConnectionNum].Connected := False;
  190. Shutdown(Handle,2);
  191. fdClose(Handle);
  192. End
  193. Else
  194. Begin
  195. PDebug(InttoStr(BufferLength)+' Bytes Recieved');
  196. {Buffer := Db_Query(Buffer);}
  197. Sent := Send(Handle,Buffer[1],Length(Buffer),0);
  198. If Sent <> Bufferlength Then
  199. PDebug('Wanted to Send : '+InttoStr(Length(Buffer))+' Only Sent : '+InttoStr(Sent));
  200. End;
  201. End;
  202. Procedure ServiceSockets;
  203. Var Loop : Integer;
  204. Begin
  205. For Loop := 1 To NumberOfConnections Do
  206. If Connection[Loop].Connected Then
  207. If FD_ISSET(Connection[Loop].Handle,FDS) Then
  208. ServiceHandle(Connection[Loop].Handle,Loop);
  209. If FD_ISSET(S,FDS) Then AcceptNewConnection;
  210. End;
  211. Procedure CloseAllOpen;
  212. Var Loop : Integer;
  213. Begin
  214. For Loop := 1 To NumberOfConnections Do
  215. Begin
  216. If Connection[Loop].Connected = True Then
  217. Begin
  218. Shutdown(Connection[Loop].Handle,1);
  219. { fdClose(Connection[Loop].Handle);}
  220. {Connection[Loop].Connected := False;}
  221. End;
  222. End;
  223. End;
  224. Begin
  225. ZeroConnection; {Clear Connected Array}
  226. Quit := False;
  227. PortNumber := 5000;
  228. SetupSocket;
  229. Repeat
  230. FD_ZERO(FDS);
  231. FD_SET(S,FDS); { Socket Looking for new connections }
  232. FD_SET(1,FDS); { Terminal }
  233. GreatestHandle := S;
  234. LoadConnectedFDS;
  235. If Select(GreatestHandle+1,@FDS,Nil,Nil,1000) > 0 Then
  236. Begin
  237. ServiceSockets;
  238. If FD_ISSET(1,FDS) Then
  239. Begin
  240. PDebug('Reading Console');
  241. Readln(Command);
  242. If Command='quit' Then quit := True;
  243. { Else Writeln(DB_Query(Command));}
  244. Command := '';
  245. End;
  246. End;
  247. {DB_Tic;} {Updates Database Internals, Needs at Least 1 run per second}
  248. Until Quit = True;
  249. CloseAllOpen;
  250. End.