Win32.UDP.Mod 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. MODULE UDP; (* AUTHOR "fof"; PURPOSE "UDP protocol for Winsock"; *)
  2. IMPORT SYSTEM, IP, (* AosWinsock, *) WSock32, KernelLog;
  3. CONST
  4. (* error codes. *)
  5. Ok* = 0; AddressInUse* = 3501; Timeout* = 3502; BufferOverflow* = 3503; AlreadyBlocked* = 3504;
  6. PortInUse* = 3501;
  7. unknown = 1; IPAdrLen = 4;
  8. NilPort* = 0; (* Dan 31.01.05 *)
  9. SendBufSize = 65536*8; (* send buffer size in bytes *)
  10. RecvBufSize = 65536*16; (* receive buffer size in bytes *)
  11. TYPE
  12. TYPE
  13. Socket* = OBJECT (* stores the state of a UDP communication endpoint. *)
  14. VAR sock: WSock32.Socket;
  15. (* Initialize the Socket (only use once per instance).
  16. Opens a socket which is dedicated to datagram services. lport is registered to receive datagrams
  17. from any port and any host. *)
  18. PROCEDURE & Open*( lport: LONGINT; VAR res: LONGINT );
  19. VAR
  20. sadr: WSock32.sockaddrIn;
  21. err: LONGINT;
  22. bufSize, len: LONGINT;
  23. BEGIN
  24. (* IF ~AosWinsock.ready THEN RETURN END; *)
  25. sock := WSock32.socket( WSock32.PFINet, WSock32.SockDGram, WSock32.IPProtoUDP );
  26. IF sock # WSock32.InvalidSocket THEN
  27. sadr.sinFamily := WSock32.PFINet; sadr.sinAddr := 0;
  28. IF lport # NilPort THEN sadr.sinPort := WSock32.htons( SHORT( lport ) ) ELSE sadr.sinPort := 0 END;
  29. res := WSock32.bind( sock, sadr, SIZEOF( WSock32.sockaddrIn ) );
  30. IF res # Ok THEN
  31. err := WSock32.WSAGetLastError(); (* AosWinsock.DispError( err ); *) SockFinalizer( SELF ); res := unknown;
  32. ELSE (* Kernel.RegisterObject( SELF, SockFinalizer, FALSE ); *) res := Ok
  33. END
  34. ELSE res := unknown;
  35. END;
  36. bufSize := SendBufSize; len := SIZEOF(LONGINT);
  37. err := WSock32.setsockopt(sock,WSock32.SOLSocket,WSock32.SOSndBuf,bufSize,len);
  38. IF (err # 0) & trace THEN
  39. KernelLog.String( "UDP.Open : failed to set send buffer size, WSock32 error code " ); KernelLog.Int(err,0); KernelLog.Ln;
  40. END;
  41. bufSize := RecvBufSize;
  42. err := WSock32.setsockopt(sock,WSock32.SOLSocket,WSock32.SORcvBuf,bufSize,SIZEOF(LONGINT));
  43. IF (err # 0) & trace THEN
  44. KernelLog.String( "UDP.Open : failed to set receive buffer size, WSock32 error code " ); KernelLog.Int(err,0); KernelLog.Ln;
  45. END;
  46. IF trace THEN
  47. KernelLog.String( "UDP.Open : " ); KernelLog.Int( lport, 1 ); KernelLog.String( "(" ); KernelLog.Int( res, 1 );
  48. KernelLog.String( ")" ); KernelLog.Ln;
  49. END;
  50. END Open;
  51. (* Send a UDP datagram to the foreign address specified by "fip" and "lport". The data is in "data[ofs..ofs+len-1]".
  52. In case of concurrent sends the datagrams are serialized.
  53. Sends len bytes of data (beginning at pos in buf) to the host specified by remIP and remPort. *)
  54. PROCEDURE Send*( fip: IP.Adr; fport: LONGINT; CONST data: ARRAY OF CHAR; ofs, len: LONGINT; VAR res: LONGINT );
  55. VAR sadr: WSock32.sockaddrIn; err: LONGINT;
  56. BEGIN
  57. ASSERT ( LEN( data ) >= (ofs + len) );
  58. IF (fip.usedProtocol = IP.IPv4) THEN
  59. SYSTEM.MOVE( ADDRESSOF( fip ), ADDRESSOF( sadr.sinAddr ), IPAdrLen );
  60. sadr.sinFamily := WSock32.PFINet; sadr.sinPort := WSock32.htons( SHORT( fport ) );
  61. res := WSock32.sendto( sock, data[ofs], len, 0, sadr, SIZEOF( WSock32.sockaddrIn ) );
  62. (* account that sendto returns number of bytes sent to the socket (Alexey) *)
  63. IF res = len THEN res := Ok; ELSE err := WSock32.WSAGetLastError(); res := unknown; END;
  64. ELSE res := unknown;
  65. END;
  66. IF trace THEN
  67. IF (fip.usedProtocol = IP.IPv4) THEN
  68. KernelLog.String( "UDP.Send : " ); KernelLog.Int( fip.ipv4Adr, 1 ); KernelLog.String( " , " ); KernelLog.Int( fport, 1 );
  69. KernelLog.String( "(" ); KernelLog.Int( res, 1 ); KernelLog.String( ")" ); KernelLog.Ln;
  70. ELSE
  71. KernelLog.String("UDP.Send : Error, only works with IPv4 addresses!"); KernelLog.Ln;
  72. END;
  73. END;
  74. END Send;
  75. (* Receive a UDP datagram. If none is available, wait up to the specified timeout for one to arrive. Only one thread
  76. is allowed to wait for a datagram. "data[ofs..ofs+size-1]" is the data buffer to hold the returned datagram.
  77. "ms" is a timeout value in milliseconds, or 0 for an indefinite wait. On return, "fip" and "lport" hold
  78. the foreign address. "len" returns the actual datagram size and "data[ofs..ofs+len-1]" returns the data.
  79. "res" returns "Timeout" in case of a timeout and "BufferOverflow" if the received datagram was too big,
  80. in which case "len" is the actual datagram size negated and the data is undefined. "res" returns "AlreadyBlocked"
  81. if another thread is already blocked on this Socket.
  82. Stores an entire datagram in buf beginning at pos. On success (S.res = done), remIP and remPort indicate the sender, len indicate the length of valid data. *)
  83. PROCEDURE Receive*( VAR data: ARRAY OF CHAR; ofs, size, ms: LONGINT; VAR fip: IP.Adr;
  84. VAR fport, len, res: LONGINT );
  85. VAR sadr: WSock32.sockaddrIn; err: LONGINT; l: LONGINT;
  86. ret: LONGINT; fdset: WSock32.FDSet; avail: BOOLEAN; time: WSock32.TimeVal;
  87. BEGIN
  88. ASSERT ( ofs+size <= LEN( data ) );
  89. l := SIZEOF( WSock32.sockaddrIn );
  90. IF ms=-1 THEN (* do, as if data was available to invoke blocking call of recvfrom *)
  91. avail := TRUE;
  92. ELSE (* handle timeout *)
  93. ret := WSock32.ioctlsocket( sock, WSock32.FIONRead, res );
  94. IF ret # 0 THEN (* error *)
  95. err := WSock32.WSAGetLastError(); res := unknown; avail := FALSE;
  96. ELSE (* no error *)
  97. avail := res > 0;
  98. IF ~avail THEN (* nothing available yet *)
  99. fdset.fdcount := 1; fdset.socket[0] := sock;
  100. IF ms = 0 THEN
  101. time := NIL
  102. ELSE
  103. NEW(time);
  104. time.sec := ms DIV 1000; time.musec := 1000* (ms MOD 1000);
  105. END;
  106. ret := WSock32.select( 0, fdset, NIL , NIL , time );
  107. avail := ret = 1;
  108. IF ~avail THEN (* still nothing available *)
  109. len := 0; res := Timeout
  110. END;
  111. END;
  112. END;
  113. END;
  114. IF avail THEN
  115. len := WSock32.recvfrom( sock, data[ofs], size, 0, sadr, l );
  116. IF len < 0 THEN err := WSock32.WSAGetLastError(); (* AosWinsock.DispError( err ); *) res := unknown;
  117. ELSE res := Ok;
  118. END;
  119. END;
  120. fport := WSock32.ntohs( sadr.sinPort );
  121. SYSTEM.MOVE( ADDRESSOF( sadr.sinAddr ), ADDRESSOF( fip ), IPAdrLen );
  122. (*? Problem - Windows XP does not fill in the Fip.UseProtocol field for IPv4 packets ! done manually here*)
  123. IF fip.ipv4Adr # 0 THEN fip.usedProtocol := IP.IPv4 END;
  124. IF trace THEN
  125. IF (fip.usedProtocol = IP.IPv4) THEN
  126. KernelLog.String( "UDP.Receive : " ); KernelLog.Int( fip.ipv4Adr, 1 ); KernelLog.String( " , " ); KernelLog.Int( fport, 1 );
  127. KernelLog.String( "(" ); KernelLog.Int( res, 1 ); KernelLog.String( ")" ); KernelLog.Ln;
  128. ELSE
  129. KernelLog.String("UDP.Receive : Warning, received UDP packet from non-IPv4 source!"); KernelLog.Ln;
  130. END;
  131. END;
  132. END Receive;
  133. (* Close the Socket, freeing its address for re-use. *)
  134. PROCEDURE Close*;
  135. BEGIN
  136. SockFinalizer( SELF );
  137. IF trace THEN KernelLog.String( "UDP.Close" ); KernelLog.Ln; END;
  138. END Close;
  139. (*
  140. (** Returns the size of the first available datagram on the socket, otherwise <= 0. *)
  141. PROCEDURE Available*( ): LONGINT;
  142. VAR avail: LONGINT;
  143. BEGIN
  144. WSock32.ioctlsocket( sock, WSock32.FIONRead, avail ); RETURN avail
  145. END Available;
  146. *)
  147. END Socket;
  148. VAR
  149. trace: BOOLEAN;
  150. PROCEDURE SockFinalizer( S: ANY );
  151. VAR ret: LONGINT; (* Dan 10.11.05 *) BEGIN
  152. WITH S: Socket DO
  153. IF S.sock # WSock32.InvalidSocket THEN ret:= WSock32.closesocket( S.sock ); S.sock := WSock32.InvalidSocket END
  154. END
  155. END SockFinalizer;
  156. PROCEDURE ToggleTrace*;
  157. BEGIN
  158. trace := ~trace;
  159. IF trace THEN KernelLog.String( "UDP: tracing ON" ); KernelLog.Ln;
  160. ELSE KernelLog.String( "UDP: tracing OFF" ); KernelLog.Ln
  161. END;
  162. END ToggleTrace;
  163. END UDP.