Blocking Calls with Timeout

Một phần của tài liệu Morgan Haupmann TCP IP Socket in C++ (Trang 101 - 105)

In the previous section we demonstrated how to check if a call would block prior to exe- cuting it. Sometimes, however, we may actually need to know that some I/O event hasnot happened for a certain time period. For example, in Chapter 2 we sawUdpEchoClientTime- outSocket.cs, where the client sends a datagram to the server and then waits to receive a response. If a datagram is not received before the timer expires,ReceiveFrom()unblocks to allow the client to handle the datagram loss. Utilizing socket options, theSocketclass supports setting a bound on the maximum time (in milliseconds) to block on sending or receiving data, using theSocketOption.SendTimeoutandSocketOption.ReceiveTimeout properties.

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

:::

sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout,

3000); // Set a 3 second timeout on Send()/SendTo() If you are using theTcpClientclass, it contains theSendTimeoutandReceiveTimeout properties which can be set or retrieved.

TcpClient client = new TcpClient(server, port);

:::

client.ReceiveTimeout = 5000; // Set a 5 second timeout on Read()

In both cases if the specified time elapses before the method returns, a Socket- Exceptionis thrown with theSocket’sErrorCodeproperty set to 10060 (connection timed out).

ThePoll()method of Socketoffers more functionality.Poll()takes two options:

an integer number ofmicroseconds (not milliseconds) to wait for a response, and amode that indicates what type of operation we are waiting for. The wait time can be negative, indicating an indefinite wait time (basically, a block). The wait time can also be zero, which allowsPoll()to be used for prechecking. The mode is set to one of theSelectMode enumeration valuesSelectRead,SelectWrite, orSelectError, depending on what we are checking for.Poll()returnstrueif the socket has an operation pending for the requested mode, orfalseif it does not.

■ 4.1 Nonblocking I/O 89

// Block for 1 second waiting for data to read or incoming connections if (sock.Poll(1000000, SelectMode.SelectRead)) {

// Socket has data to read or an incoming connection } else {

// No data to read or incoming connections }

In general, polling is considered very inefficient because it requires repeated calls to check status. This is sometimes called “busy waiting,” because it involves continu- ously looping back to check for events that probably happen infrequently (at least in relation to the number of checks made). Some ways to avoid polling are discussed later in this chapter, including using the Socket method Select(), which allows blocking on multiple sockets at once (Section 4.2), threads (Section 4.3), and asynchronous I/O (Section 4.4).

AWrite() orSend() call blocks until the last byte written is copied into the TCP implementation’s local buffer; if the available buffer space is smaller than the size of the write, some data must be successfully transferred to the other end of the connection before the call will return (see Section 5.1 for details). Thus, the amount of time that a large data send may block is controlled by the receiving application. Therefore, any protocol that sends a large enough amount of data over a socket instance can block for an unlimited amount of time. (See Section 5.2 for further discussion on the consequences of this.)

Establishing aSocketconnection to a specified host and port will block until either the connection is established, the connection is refused, or a system-imposed timeout occurs. The system-imposed timeout is long (on the order of minutes), and C# does not provide any means of shortening it.

Suppose we want to implement the echo server with a limit on the amount of time taken to service each client. That is, we define a target,TIMELIMIT, and implement the server in such a way that afterTIMELIMITmilliseconds, the server instance is terminated.

One approach simply has the server instance keep track of the amount of the remain- ing time, and use the send and receive timeout settings described above to ensure that reads and writes do not block for longer than that time. TcpEchoServerTimeout.cs implements this approach.

TcpEchoServerTimeout.cs

0 using System; // For Console, Int32, ArgumentException, Environment 1 using System.Net; // For IPAddress

2 using System.Net.Sockets; // For TcpListener, TcpClient 3

4 class TcpEchoServerTimeout { 5

6 private const int BUFSIZE = 32; // Size of receive buffer

7 private const int BACKLOG = 5; // Outstanding conn queue max size 8 private const int TIMELIMIT = 10000; // Default time limit (ms)

9

10 static void Main(string[] args) { 11

12 if (args.Length > 1) // Test for correct # of args 13 throw new ArgumentException("Parameters: [<Port>]");

14

15 int servPort = (args.Length == 1) ? Int32.Parse(args[0]): 7;

16

17 Socket server = null;

18

19 try {

20 // Create a socket to accept client connections

21 server = new Socket(AddressFamily.InterNetwork, SocketType.Stream,

22 ProtocolType.Tcp);

23

24 server.Bind(new IPEndPoint(IPAddress.Any, servPort));

25

26 server.Listen(BACKLOG);

27 } catch (SocketException se) {

28 Console.WriteLine(se.ErrorCode + ": " + se.Message);

29 Environment.Exit(se.ErrorCode);

30 }

31

32 byte[] rcvBuffer = new byte[BUFSIZE]; // Receive buffer

33 int bytesRcvd; // Received byte count

34 int totalBytesEchoed = 0; // Total bytes sent 35

36 for (;;) { // Run forever, accepting and servicing connections 37

38 Socket client = null;

39

40 try {

41

42 client = server.Accept(); // Get client connection 43

44 DateTime starttime = DateTime.Now;

45

46 // Set the ReceiveTimeout

47 client.SetSocketOption(SocketOptionLevel.Socket,

48 SocketOptionName.ReceiveTimeout,

49 TIMELIMIT);

■ 4.1 Nonblocking I/O 91

50

51 Console.Write("Handling client at " + client.RemoteEndPoint + " - ");

52

53 // Receive until client closes connection, indicated by 0 return value 54 totalBytesEchoed = 0;

55 while ((bytesRcvd = client.Receive(rcvBuffer, 0, rcvBuffer.Length,

56 SocketFlags.None)) > 0) {

57 client.Send(rcvBuffer, 0, bytesRcvd, SocketFlags.None);

58 totalBytesEchoed += bytesRcvd;

59

60 // Check elapsed time

61 TimeSpan elapsed = DateTime.Now - starttime;

62 if (TIMELIMIT - elapsed.TotalMilliseconds < 0) {

63 Console.WriteLine("Aborting client, timelimit " + TIMELIMIT + 64 "ms exceeded; echoed " + totalBytesEchoed + " bytes");

65 client.Close();

66 throw new SocketException(10060);

67 }

68

69 // Set the ReceiveTimeout

70 client.SetSocketOption(SocketOptionLevel.Socket,

71 SocketOptionName.ReceiveTimeout,

72 (int)(TIMELIMIT - elapsed.TotalMilliseconds));

73 }

74 Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);

75

76 client.Close(); // Close the socket. We are done with this client!

77

78 } catch (SocketException se) {

79 if (se.ErrorCode == 10060) { // WSAETIMEDOUT: Connection timed out 80 Console.WriteLine("Aborting client, timelimit " + TIMELIMIT +

81 "ms exceeded; echoed " + totalBytesEchoed + " bytes");

82 } else {

83 Console.WriteLine(se.ErrorCode + ": " + se.Message);

84 }

85 client.Close();

86 }

87 }

88 } 89 }

TcpEchoServerTimeout.cs

1. Argument parsing and setup:lines 12–17

2. Create socket, callBind()andListen:lines 19–30 3. Main server loop:lines 36–87

Accept client connection:line 42

Record start time:line 44

Set initial timeout:lines 46–47

Set the initial Receive() timeout to theTIMELIMIT since minimal time should not have elapsed yet.

Receive loop:lines 55–73

Receive data and send echo reply. After each receive and send, update and check the elapsed time and abort if necessary. To abort we throw the same exception a timeout during the Receive() would throw, which is a SocketExceptionwith ErrorCode10060. If we have not exceeded our timeout after the data transfer, reset theReceive()timeout based on our new elapsed time before we loop around to receive more data.

Successful completion:lines 74–76

If we successfully echo all the bytes within the timelimit, output the echoed byte length and close the client socket.

Exception handling:lines 78–86

If we hit a timeout limit, output the appropriate message. Close the client socket and allow the receive loop to continue and handle more clients.

Một phần của tài liệu Morgan Haupmann TCP IP Socket in C++ (Trang 101 - 105)

Tải bản đầy đủ (PDF)

(188 trang)