1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.socket
;
5 import com
.google
.appengine
.api
.socket
.AppEngineSocketOptions
.Option
;
6 import com
.google
.appengine
.api
.socket
.SocketServicePb
.AcceptReply
;
7 import com
.google
.appengine
.api
.socket
.SocketServicePb
.AcceptRequest
;
8 import com
.google
.appengine
.api
.socket
.SocketServicePb
.AddressPort
;
9 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CloseReply
;
10 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CloseRequest
;
11 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ConnectReply
;
12 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ConnectRequest
;
13 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketReply
;
14 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
;
15 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
.SocketFamily
;
16 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
.SocketProtocol
;
17 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketNameReply
;
18 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketNameRequest
;
19 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketOptionsReply
;
20 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketOptionsRequest
;
21 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ListenReply
;
22 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ListenRequest
;
23 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ReceiveReply
;
24 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ReceiveRequest
;
25 import com
.google
.appengine
.api
.socket
.SocketServicePb
.RemoteSocketServiceError
;
26 import com
.google
.appengine
.api
.socket
.SocketServicePb
.RemoteSocketServiceError
.ErrorCode
;
27 import com
.google
.appengine
.api
.socket
.SocketServicePb
.RemoteSocketServiceError
.SystemError
;
28 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SendReply
;
29 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SendRequest
;
30 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SetSocketOptionsReply
;
31 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SetSocketOptionsRequest
;
32 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ShutDownReply
;
33 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ShutDownRequest
;
34 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SocketOption
.SocketOptionName
;
35 import com
.google
.apphosting
.api
.ApiProxy
;
37 import java
.io
.IOException
;
38 import java
.io
.InputStream
;
39 import java
.io
.OutputStream
;
40 import java
.io
.Serializable
;
41 import java
.net
.ConnectException
;
42 import java
.net
.Inet4Address
;
43 import java
.net
.Inet6Address
;
44 import java
.net
.InetAddress
;
45 import java
.net
.InetSocketAddress
;
46 import java
.net
.SocketAddress
;
47 import java
.net
.SocketException
;
48 import java
.net
.SocketImpl
;
49 import java
.net
.SocketOptions
;
50 import java
.net
.SocketTimeoutException
;
51 import java
.net
.UnknownHostException
;
52 import java
.util
.Arrays
;
53 import java
.util
.concurrent
.atomic
.AtomicLong
;
56 * Implements the {@link SocketImpl} interface for App Engine based sockets.
59 class AppEngineSocketImpl
extends SocketImpl
implements AppEngineSocketOptionsClient
, Serializable
{
60 private static final long serialVersionUID
= -2683691443688405980L;
62 static enum SocketState
{
64 * Socket is uninitialized.
69 * {@link #create(boolean)} has been called.
70 * Note that this does not relate to remote socket create.
80 * Socket is connected.
85 * Socket is listening.
95 SocketState currentState
= SocketState
.UNINITIALIZED
;
97 private SocketApiHelper socketHelper
= null;
99 String descriptor
= null;
101 SocketFamily socketFamily
;
105 private boolean readsShutdown
= false;
106 private boolean writesShutdown
= false;
108 private final AtomicLong sendOffset
= new AtomicLong(0);
110 private AppEngineSocketInputStream socketInputStream
= null;
113 * True indicates that this socket is for a (TCP) stream socket otherwise
114 * it is a datagram socket (UDP).
116 private boolean stream
;
118 private InetAddress localAddress
;
120 AppEngineSocketImpl() {
123 AppEngineSocketImpl(SocketApiHelper socketHelper
) {
124 this.socketHelper
= socketHelper
;
127 SocketApiHelper
getSocketApiHelper() {
128 return socketHelper
== null ? AppEngineSocketImplFactory
.SOCKET_API_HELPER
: socketHelper
;
131 protected InetAddress
toInetAddress(AddressPort addressPort
) throws UnknownHostException
{
132 switch (socketFamily
== null ? SocketFamily
.IPv4
: socketFamily
) {
134 return Inet4Address
.getByAddress(null, addressPort
.getPackedAddressAsBytes());
137 return Inet6Address
.getByAddress(null, addressPort
.getPackedAddressAsBytes(), null);
140 throw new ApiProxy
.ApplicationException(
141 ErrorCode
.INVALID_REQUEST
.getValue(),
142 "Invalid socket family : " + socketFamily
);
148 AppEngineSocketOptions
.Option option
, Object value
) throws SocketException
{
150 option
.validateAndApply(this, value
);
154 * @see java.net.SocketOptions#setOption(int, java.lang.Object)
157 public void setOption(int opt
, Object value
) throws SocketException
{
158 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(opt
);
159 if (option
== null) {
160 throw new SocketException("unrecognized socket option: " + opt
);
162 setOption(option
, value
);
165 String
statesToString(SocketState
...socketStates
) {
166 StringBuilder builder
= new StringBuilder();
168 for (SocketState state
: socketStates
) {
169 builder
.append(prefix
);
171 builder
.append(state
.toString());
173 return builder
.toString();
176 void checkStateIsOneOf(String errorMsg
, SocketState
...socketStates
) throws SocketException
{
177 for (SocketState state
: socketStates
) {
178 if (currentState
== state
) {
182 throw new SocketException(errorMsg
+ ": Expected to be in one of states : (" +
183 statesToString(socketStates
) + "), was in state " + currentState
);
186 void checkStateIsNotOneOf(String errorMsg
, SocketState
...socketStates
) throws SocketException
{
187 for (SocketState state
: socketStates
) {
188 if (currentState
== state
) {
189 throw new SocketException(errorMsg
+ ": Expected to not be in one of (" +
190 statesToString(socketStates
) + "), was in state " + currentState
);
196 * Checks that the socket is not closed.
198 private void checkNotClosed() throws SocketException
{
199 checkStateIsNotOneOf("Socket is closed", SocketState
.UNINITIALIZED
, SocketState
.CLOSED
);
203 * Implements the remote service SetSocketOptions call.
206 public void setSocketOptionAsBytes(AppEngineSocketOptions
.Option opt
, byte[] value
)
207 throws SocketException
{
208 SocketOptionName name
= opt
.getOptName();
212 checkStateIsNotOneOf("Socket is closed",
213 SocketState
.UNINITIALIZED
, SocketState
.CREATE_CALLED
, SocketState
.CLOSED
);
214 SetSocketOptionsRequest request
= new SetSocketOptionsRequest();
215 SetSocketOptionsReply response
= new SetSocketOptionsReply();
216 request
.setSocketDescriptor(descriptor
);
217 request
.addOptions().setLevel(opt
.getLevel()).setOption(name
).setValueAsBytes(value
);
219 getSocketApiHelper().makeSyncCall("SetSocketOptions", request
, response
, null);
223 public void setTimeout(int timeout
) {
228 * @see java.net.SocketOptions#getOption(int)
231 public Object
getOption(int optID
) throws SocketException
{
232 if (SocketOptions
.SO_BINDADDR
== optID
) {
236 if (SocketOptions
.SO_TIMEOUT
== optID
) {
238 return soTimeout
<= 0 ?
0 : soTimeout
;
241 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(optID
);
242 if (option
== null) {
243 throw new SocketException("unrecognized socket option: " + optID
);
247 return getOption(option
);
248 } catch (SocketException e
) {
249 if (SocketOptions
.SO_LINGER
== optID
) {
257 * Returns the value of the given option.
258 * @throws SocketException
260 private Object
getOption(Option option
) throws SocketException
{
261 return option
.getOption(this);
265 * Called by {@link Option#getOption(AppEngineSocketImpl)}.
266 * @see AppEngineSocketOptionsClient#getSocketOptionAsBytes(Option)
269 public byte[] getSocketOptionAsBytes(Option option
) throws SocketException
{
270 GetSocketOptionsRequest request
= new GetSocketOptionsRequest();
271 GetSocketOptionsReply response
= new GetSocketOptionsReply();
272 request
.setSocketDescriptor(descriptor
);
273 request
.addOptions().setLevel(option
.getLevel()).setOption(option
.getOptName());
274 getSocketApiHelper().makeSyncCall("GetSocketOptions", request
, response
, null);
275 return response
.optionss().get(0).getValueAsBytes();
279 * @see java.net.SocketImpl#create(boolean)
282 protected synchronized void create(boolean stream
) throws IOException
{
283 checkStateIsOneOf("Socket is already created", SocketState
.UNINITIALIZED
);
284 currentState
= SocketState
.CREATE_CALLED
;
285 this.stream
= stream
;
289 * @see java.net.SocketImpl#connect(java.lang.String, int)
292 protected synchronized void connect(String host
, int port
) throws IOException
{
294 InetAddress address
= InetAddress
.getByName(host
);
295 connectToAddress(address
, port
, null);
296 } catch (UnknownHostException e
) {
303 * @see java.net.SocketImpl#connect(java.net.InetAddress, int)
306 protected synchronized void connect(InetAddress address
, int port
) throws IOException
{
307 if (address
== null) {
308 throw new IllegalArgumentException("null address is illegal for connect");
311 checkStateIsOneOf("socket is not created", SocketState
.CREATE_CALLED
, SocketState
.BOUND
);
313 connectToAddress(address
, port
, null);
317 * @see java.net.SocketImpl#connect(java.net.SocketAddress, int)
320 protected synchronized void connect(SocketAddress socketAddress
, int timeout
) throws IOException
{
321 if (socketAddress
== null) {
322 throw new IllegalArgumentException("null address is illegal for connect");
325 if (!(socketAddress
instanceof InetSocketAddress
)) {
326 throw new IllegalArgumentException("Address must be of type InetSocketAddress");
329 InetSocketAddress addr
= (InetSocketAddress
) socketAddress
;
331 if (addr
.isUnresolved()) {
332 throw new UnknownHostException(addr
.getHostName());
335 connectToAddress(addr
.getAddress(), addr
.getPort(), timeout
== 0 ?
null : timeout
);
338 private void connectToAddress(InetAddress address
, int port
, Integer timeoutMillis
)
339 throws SocketException
, SocketTimeoutException
{
340 switch (currentState
) {
341 case CREATE_CALLED
: {
342 if (timeoutMillis
== null) {
343 createSocket(null, 0, address
, port
, true);
345 createSocket(null, 0, address
, port
, false);
346 connectSocket(address
, port
, timeoutMillis
);
351 connectSocket(address
, port
, timeoutMillis
);
360 this.address
= address
;
363 private void processConnectError(RemoteSocketServiceError serviceError
)
364 throws SocketException
, SocketTimeoutException
{
366 if (SystemError
.valueOf(serviceError
.getSystemError()) == SystemError
.SYS_EINPROGRESS
) {
367 throw new SocketTimeoutException();
369 if (SystemError
.valueOf(serviceError
.getSystemError()) == SystemError
.SYS_ETIMEDOUT
) {
370 throw new SocketTimeoutException();
372 if (SystemError
.valueOf(serviceError
.getSystemError()) == SystemError
.SYS_ECONNREFUSED
) {
373 throw new ConnectException(serviceError
.getErrorDetail());
375 throw SocketApiHelper
.translateError(
376 ErrorCode
.SYSTEM_ERROR
.getValue(),
377 "errno: " + serviceError
.getSystemError() + ", detail:" + serviceError
.getErrorDetail());
381 * Performs connect given a timeout.
383 private void connectSocket(InetAddress remoteAddress
, int remotePort
, Integer timeoutMillis
)
384 throws SocketException
, SocketTimeoutException
{
385 if (remoteAddress
== null) {
386 throw new IllegalArgumentException("remoteAddress must not be null if connect requested");
389 ConnectRequest request
= new ConnectRequest().setSocketDescriptor(descriptor
);
390 ConnectReply response
= new ConnectReply();
392 request
.getMutableRemoteIp().setPort(remotePort
)
393 .setPackedAddressAsBytes(remoteAddress
.getAddress());
394 if (timeoutMillis
!= null) {
395 request
.setTimeoutSeconds(0.001D
* timeoutMillis
);
398 RemoteSocketServiceError serviceError
= new RemoteSocketServiceError();
399 if (!getSocketApiHelper().makeSyncCall("Connect", request
, response
, serviceError
)) {
400 processConnectError(serviceError
);
403 currentState
= SocketState
.CONNECTED
;
408 * Releases the socket and absorbs any exceptions.
410 private void releaseSocket() {
411 readsShutdown
= false;
412 writesShutdown
= false;
415 } catch (IOException ignored
) {
417 currentState
= SocketState
.CLOSED
;
420 private void fixLocalAddress() throws SocketException
{
421 GetSocketNameRequest request
= new GetSocketNameRequest().setSocketDescriptor(descriptor
);
422 GetSocketNameReply response
= new GetSocketNameReply();
424 getSocketApiHelper().makeSyncCall("GetSocketName", request
, response
, null);
425 AddressPort externalIp
= response
.getProxyExternalIp();
426 this.localport
= externalIp
.getPort();
429 localAddress
= toInetAddress(externalIp
);
430 } catch (UnknownHostException e
) {
431 throw new SocketException(e
.toString());
436 * Create the remote socket with an optional bind and connect. Either the
437 * remoteAddress or bindAddress or both parameters must be provided.
438 * It is used to determine if socket is to be created with an IPV4 or IPV6
440 * @throws SocketException
441 * @throws SocketTimeoutException
443 private void createSocket(
444 InetAddress bindAddress
, int bindPort
, InetAddress remoteAddress
,
445 int remotePort
, boolean connect
)
446 throws SocketException
, SocketTimeoutException
{
448 CreateSocketRequest request
= new CreateSocketRequest();
449 CreateSocketReply response
= new CreateSocketReply();
451 if (bindAddress
== null && remoteAddress
== null) {
452 throw new IllegalArgumentException("Addresses must not be null.");
455 request
.setProtocol(stream ? SocketProtocol
.TCP
: SocketProtocol
.UDP
);
457 if (bindAddress
!= null) {
458 if (bindAddress
instanceof Inet6Address
) {
459 socketFamily
= SocketFamily
.IPv6
;
460 request
.setFamily(socketFamily
);
461 if (remoteAddress
!= null && !(remoteAddress
instanceof Inet6Address
)) {
462 throw new IllegalArgumentException(
463 "Cannot specify remote and local addresses of different family.");
465 } else if (bindAddress
instanceof Inet4Address
) {
466 socketFamily
= SocketFamily
.IPv4
;
467 request
.setFamily(socketFamily
);
468 if (remoteAddress
!= null && !(remoteAddress
instanceof Inet4Address
)) {
469 throw new IllegalArgumentException(
470 "Cannot specify remote and local addresses of different family.");
473 throw new SocketException("Unknown InetAddress type: " + bindAddress
.getClass().getName());
476 request
.getMutableProxyExternalIp().setPort(bindPort
)
477 .setPackedAddressAsBytes(bindAddress
.getAddress());
479 if (remoteAddress
instanceof Inet6Address
) {
480 socketFamily
= SocketFamily
.IPv6
;
481 request
.setFamily(SocketFamily
.IPv6
);
482 } else if (remoteAddress
instanceof Inet4Address
) {
483 socketFamily
= SocketFamily
.IPv4
;
484 request
.setFamily(SocketFamily
.IPv4
);
486 throw new SocketException(
487 "Unknown InetAddress type: " + remoteAddress
.getClass().getName());
492 if (remoteAddress
== null) {
493 throw new IllegalArgumentException("remoteAddress must not be null if connect requested");
495 request
.getMutableRemoteIp().setPort(remotePort
)
496 .setPackedAddressAsBytes(remoteAddress
.getAddress());
497 RemoteSocketServiceError serviceError
= new RemoteSocketServiceError();
498 if (!getSocketApiHelper().makeSyncCall("CreateSocket", request
, response
, serviceError
)) {
499 processConnectError(serviceError
);
502 getSocketApiHelper().makeSyncCall("CreateSocket", request
, response
, null);
505 descriptor
= response
.getSocketDescriptor();
506 currentState
= connect ? SocketState
.CONNECTED
: SocketState
.BOUND
;
514 * @see java.net.SocketImpl#bind(java.net.InetAddress, int)
517 protected synchronized void bind(InetAddress host
, int port
) throws IOException
{
518 createSocket(host
, port
, null, 0, false);
519 currentState
= SocketState
.BOUND
;
523 * @see java.net.SocketImpl#listen(int)
526 protected void listen(int backlog
) throws IOException
{
527 checkStateIsNotOneOf("Socket must be in a bound state.", SocketState
.UNINITIALIZED
,
528 SocketState
.CREATE_CALLED
, SocketState
.CONNECTED
, SocketState
.CLOSED
);
529 ListenRequest request
= new ListenRequest().setSocketDescriptor(descriptor
).setBacklog(backlog
);
530 getSocketApiHelper().makeSyncCall("Listen", request
, new ListenReply(), null);
531 currentState
= SocketState
.LISTEN
;
535 * @see java.net.SocketImpl#accept(java.net.SocketImpl)
538 protected void accept(SocketImpl s
) throws IOException
{
539 checkStateIsOneOf("Socket is not in passive (accepting) mode.", SocketState
.LISTEN
);
540 if (!(s
instanceof AppEngineSocketImpl
)) {
541 throw new IllegalStateException(
542 "Expected a SocketImpl compatable with '" + this.getClass().getName() +
543 "'. A '" + s
.getClass().getName() + "' was received.");
546 AppEngineSocketImpl acceptingSocket
= (AppEngineSocketImpl
) s
;
547 AcceptReply response
= new AcceptReply();
548 AcceptRequest request
= new AcceptRequest().setSocketDescriptor(descriptor
);
549 getSocketApiHelper().makeSyncCall("Accept", request
, response
, null);
550 acceptingSocket
.doAccept(response
);
554 * Initializes an AppEngineSocketImpl from an AcceptReply message.
556 private void doAccept(AcceptReply response
) {
557 switch (currentState
) {
569 descriptor
= response
.getNewSocketDescriptor();
570 AddressPort addressPort
= response
.getRemoteAddress();
572 switch (addressPort
.getPackedAddressAsBytes().length
) {
574 socketFamily
= SocketFamily
.IPv4
;
575 address
= Inet4Address
.getByAddress(null, addressPort
.getPackedAddressAsBytes());
579 socketFamily
= SocketFamily
.IPv6
;
580 address
= Inet6Address
.getByAddress(null, addressPort
.getPackedAddressAsBytes(), null);
584 throw new IllegalStateException("Unexpected network address type.");
587 } catch (UnknownHostException e
) {
588 throw new IllegalStateException(
589 "Unexpected UnknownHostException thrown unexpectedly: " + e
.getMessage());
591 port
= addressPort
.getPort();
592 currentState
= SocketState
.CONNECTED
;
596 * @see java.net.SocketImpl#getInputStream()
599 protected InputStream
getInputStream() throws IOException
{
600 if (currentState
== SocketState
.CLOSED
) {
601 throw new IOException("Socket closed.");
605 throw new IOException("Socket input is shutdown.");
608 if (socketInputStream
== null) {
609 socketInputStream
= new AppEngineSocketInputStream(this);
611 return socketInputStream
;
615 * @see java.net.SocketImpl#getOutputStream()
618 protected OutputStream
getOutputStream() throws IOException
{
619 if (currentState
== SocketState
.CLOSED
) {
620 throw new IOException("Socket closed.");
623 if (writesShutdown
) {
624 throw new IOException("Socket output is shutdown.");
627 return new AppEngineSocketOutputStream(this);
631 * @see java.net.SocketImpl#available()
634 protected int available() throws IOException
{
639 * @see java.net.SocketImpl#close()
642 protected void close() throws IOException
{
643 if (descriptor
!= null) {
645 CloseRequest request
= new CloseRequest().setSocketDescriptor(descriptor
)
646 .setSendOffset(sendOffset
.get());
647 getSocketApiHelper().makeSyncCall("Close", request
, new CloseReply(), null);
649 currentState
= SocketState
.CLOSED
;
656 * @see java.net.SocketImpl#sendUrgentData(int)
659 protected void sendUrgentData(int data
) throws IOException
{
660 throw new IllegalStateException(
661 "AppEngineSocketImpl#sendUrgentData() function is unimplemented.");
665 * @see java.net.SocketImpl#shutdownInput
668 protected void shutdownInput() throws IOException
{
669 if (currentState
== SocketState
.CONNECTED
&& !readsShutdown
) {
670 ShutDownRequest request
= new ShutDownRequest().setSocketDescriptor(descriptor
)
671 .setSendOffset(sendOffset
.get())
672 .setHow(ShutDownRequest
.How
.SOCKET_SHUT_RD
);
673 getSocketApiHelper().makeSyncCall("ShutDown", request
, new ShutDownReply(), null);
674 readsShutdown
= true;
679 * @see java.net.SocketImpl#shutdownOutput
682 protected void shutdownOutput() throws IOException
{
683 if (currentState
== SocketState
.CONNECTED
&& !writesShutdown
) {
684 ShutDownRequest request
= new ShutDownRequest().setSocketDescriptor(descriptor
)
685 .setSendOffset(sendOffset
.get())
686 .setHow(ShutDownRequest
.How
.SOCKET_SHUT_WR
);
687 getSocketApiHelper().makeSyncCall("ShutDown", request
, new ShutDownReply(), null);
688 writesShutdown
= true;
693 * Used by {@link AppEngineSocketOutputStream} to send data.
695 * @param buf Buffer of bytes to write.
696 * @param off Offset into the buffer.
697 * @param len The length of bytes to write.
698 * @throws IOException On error conditions
700 protected void send(byte buf
[], int off
, int len
) throws IOException
{
701 checkStateIsNotOneOf("Socket is closed.",
702 SocketState
.UNINITIALIZED
, SocketState
.CREATE_CALLED
, SocketState
.CLOSED
);
703 if (writesShutdown
) {
704 throw new IOException("Socket output is shutdown.");
707 byte copy
[] = Arrays
.copyOfRange(buf
, off
, off
+ len
);
708 SendRequest request
= new SendRequest().setSocketDescriptor(descriptor
)
709 .setStreamOffset(sendOffset
.getAndAdd(copy
.length
))
710 .setDataAsBytes(copy
);
712 request
.setTimeoutSeconds(soTimeout
* 0.001);
715 RemoteSocketServiceError serviceError
= new RemoteSocketServiceError();
716 if (!getSocketApiHelper().makeSyncCall("Send", request
, new SendReply(), serviceError
)) {
717 if (serviceError
.getSystemError() == SystemError
.SYS_EAGAIN
.getValue()) {
718 throw new SocketTimeoutException("Write timed out");
720 throw SocketApiHelper
.translateError(
721 ErrorCode
.SYSTEM_ERROR
.getValue(),
722 "errno: " + serviceError
.getSystemError() +
723 ", detail:" + serviceError
.getErrorDetail());
729 * This is wrapped by {@link AppEngineSocketInputStream} and has the
730 * same semantics as {@link java.io.InputStream#read(byte[], int, int)}.
731 * @throws IOException On error conditions
733 protected int receive(byte[] buf
, int off
, int len
) throws IOException
{
734 checkStateIsNotOneOf("Socket is closed.",
735 SocketState
.UNINITIALIZED
, SocketState
.CREATE_CALLED
, SocketState
.CLOSED
);
738 throw new IOException("Socket input is shutdown.");
741 if (len
< 0 || off
< 0 || buf
.length
- off
< len
) {
745 throw new ArrayIndexOutOfBoundsException();
748 ReceiveReply response
= new ReceiveReply();
749 ReceiveRequest request
= new ReceiveRequest().setSocketDescriptor(descriptor
)
752 request
.setTimeoutSeconds(soTimeout
* 0.001);
754 RemoteSocketServiceError serviceError
= new RemoteSocketServiceError();
755 if (!getSocketApiHelper().makeSyncCall("Receive", request
, response
, serviceError
)) {
756 if (serviceError
.getSystemError() == SystemError
.SYS_EAGAIN
.getValue()) {
757 throw new SocketTimeoutException("Read timed out");
759 throw SocketApiHelper
.translateError(
760 ErrorCode
.SYSTEM_ERROR
.getValue(),
761 "errno: " + serviceError
.getSystemError() +
762 ", detail:" + serviceError
.getErrorDetail());
766 byte readBytes
[] = response
.getDataAsBytes();
768 if (readBytes
.length
> 0) {
769 System
.arraycopy(readBytes
, 0, buf
, off
, readBytes
.length
);
774 return readBytes
.length
;