From 2b521fa72ea1b694ec6fa9893ebec98979e4da68 Mon Sep 17 00:00:00 2001 From: Nic Ferrier Date: Tue, 8 Jan 2002 21:14:58 +0000 Subject: [PATCH] natPlainSocketImpl.cc: Added timeout handling for sockets. 2002-01-08 Nic Ferrier * java/net/natPlainSocketImpl.cc: Added timeout handling for sockets. (close): New function closes the socket. (write): New functions for output to socket. (read): New functions for reading from socket. * java/net/PlainSocketImpl.java: Glue for new timeout implementation. (write): Call the native impl. (read): Likewise. (getInputStream): Get a stream to read from the socket. (getOutputStream): Get a stream to write to the socket. From-SVN: r48662 --- libjava/ChangeLog | 14 ++ libjava/java/net/PlainSocketImpl.java | 129 +++++++++++++++-- libjava/java/net/natPlainSocketImpl.cc | 253 ++++++++++++++++++++++++++++++++- 3 files changed, 381 insertions(+), 15 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index db22179..a5e7fa7 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,17 @@ +2002-01-08 Nic Ferrier + + * java/net/natPlainSocketImpl.cc: Added timeout handling for + sockets. + (close): New function closes the socket. + (write): New functions for output to socket. + (read): New functions for reading from socket. + * java/net/PlainSocketImpl.java: Glue for new timeout + implementation. + (write): Call the native impl. + (read): Likewise. + (getInputStream): Get a stream to read from the socket. + (getOutputStream): Get a stream to write to the socket. + 2002-01-08 Tom Tromey * resolve.cc (_Jv_PrepareClass): Enable verifier. diff --git a/libjava/java/net/PlainSocketImpl.java b/libjava/java/net/PlainSocketImpl.java index 49155d9..81df487 100644 --- a/libjava/java/net/PlainSocketImpl.java +++ b/libjava/java/net/PlainSocketImpl.java @@ -1,6 +1,6 @@ // PlainSocketImpl.java - Implementation of SocketImpl. -/* Copyright (C) 1999 Free Software Foundation +/* Copyright (C) 1999 , 2002 Free Software Foundation This file is part of libgcj. @@ -11,17 +11,16 @@ details. */ package java.net; import java.io.*; -/** - * @author Per Bothner - * @date February 22, 1999. - */ /** + * The standard GCJ socket implementation. * Written using on-line Java Platform 1.2 API Specification, as well * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). * Status: Believed complete and correct. + * + * @author Per Bothner + * @author Nic Ferrier */ - class PlainSocketImpl extends SocketImpl { // These fields are mirrored for use in native code to avoid cpp conflicts @@ -35,6 +34,18 @@ class PlainSocketImpl extends SocketImpl _Jv_SO_SNDBUF_ = SocketOptions.SO_SNDBUF, _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF; + /** + * The OS file handle representing the socket. + * This is used for reads and writes to/from the socket and + * to close it. + * + * {@link SocketImpl#fd} is created from this like so: + *
+   *   fd = new FileDescriptor (fnum);
+   * 
+ * + * When the socket is closed this is reset to -1. + */ int fnum = -1; // This value is set/read by setOption/getOption. @@ -62,37 +73,127 @@ class PlainSocketImpl extends SocketImpl protected native void listen (int backlog) throws IOException; private native void accept (PlainSocketImpl s) throws IOException; + protected void accept (SocketImpl s) throws IOException { accept((PlainSocketImpl) s); } + protected native int available() throws IOException; + + protected native void close () throws IOException; + + + // Stream handling. + + /** A cached copy of the in stream for reading from the socket. */ private InputStream in; + + /** A cached copy of the out stream for writing to the socket. */ private OutputStream out; + + // The native read methods. + + private native int read() throws IOException; + + private native int read(byte[] buffer, int offset, int count) + throws IOException; + + + // The native write methods. + + private native void write(int c) throws IOException; + + private native void write(byte[] buffer, int offset, int count) + throws IOException; + + + /** @return the input stream attached to the socket. + */ protected InputStream getInputStream() throws IOException { - // FIXME: TODO - Implement class SocketInputStream timeouts in read(); if (in == null) - in = new FileInputStream (fd); + in = new SocketInputStream(); return in; } + /** @return the output stream attached to the socket. + */ protected OutputStream getOutputStream() throws IOException { if (out == null) - out = new FileOutputStream (fd); + out = new SocketOutputStream(); return out; } - protected int available () throws IOException + /** + * A stream which reads from the socket implementation. + * + * @author Nic Ferrier + */ + class SocketInputStream + extends InputStream { - return in.available(); + SocketInputStream() + { + } + + public final void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + public final int available() throws IOException + { + return PlainSocketImpl.this.available(); + } + + public final int read() throws IOException + { + return PlainSocketImpl.this.read(); + } + + public final int read(byte[] buffer, int offset, int length) + throws IOException + { + return PlainSocketImpl.this.read(buffer, offset, length); + } + + public final int read(byte[] buffer) + throws IOException + { + return PlainSocketImpl.this.read(buffer, 0, buffer.length); + } } - protected void close () throws IOException + /** A stream which writes to the socket implementation. + * + * @author Nic Ferrier + */ + class SocketOutputStream + extends OutputStream { - if (fd.valid()) - fd.close(); + public final void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + public final void write(int c) throws IOException + { + PlainSocketImpl.this.write(c); + } + + public final void write(byte[] buffer, int offset, int length) + throws IOException + { + PlainSocketImpl.this.write(buffer, offset, length); + } + + public final void write(byte[] buffer) + throws IOException + { + PlainSocketImpl.this.write(buffer, 0, buffer.length); + } } } diff --git a/libjava/java/net/natPlainSocketImpl.cc b/libjava/java/net/natPlainSocketImpl.cc index 0367d4f..cc8dca1 100644 --- a/libjava/java/net/natPlainSocketImpl.cc +++ b/libjava/java/net/natPlainSocketImpl.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000 , 2002 Free Software Foundation This file is part of libgcj. @@ -85,6 +85,9 @@ _Jv_accept (int fd, struct sockaddr *addr, socklen_t *addrlen) #include #include #include +#include +#include +#include #define BooleanClass java::lang::Boolean::class$ @@ -327,6 +330,254 @@ java::net::PlainSocketImpl::accept (java::net::PlainSocketImpl *s) throw new java::io::IOException (JvNewStringUTF (strerr)); } +// Close(shutdown) the socket. +void +java::net::PlainSocketImpl::close() +{ + // should we use shutdown here? how would that effect so_linger? + int res = ::close (fnum); + + if (res == -1) + { + // These three errors are not errors according to tests performed + // on the reference implementation. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new java::io::IOException (JvNewStringUTF (strerror (errno))); + } + // Safe place to reset the file pointer. + fnum = -1; +} + +// Write a byte to the socket. +void +java::net::PlainSocketImpl::write(jint b) +{ + jbyte d =(jbyte) b; + int r = 0; + + while (r != 1) + { + r = ::write (fnum, &d, 1); + if (r == -1) + { + if (java::lang::Thread::interrupted()) + { + java::io::InterruptedIOException *iioe + = new java::io::InterruptedIOException + (JvNewStringLatin1 (strerror (errno))); + iioe->bytesTransferred = 0; + throw iioe; + } + // Some errors should not cause exceptions. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new java::io::IOException (JvNewStringUTF (strerror (errno))); + } + } +} + +// Write some bytes to the socket. +void +java::net::PlainSocketImpl::write(jbyteArray b, jint offset, jint len) +{ + if (! b) + throw new java::lang::NullPointerException; + if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b)) + throw new java::lang::ArrayIndexOutOfBoundsException; + + jbyte *bytes = elements (b) + offset; + int written = 0; + while (len > 0) + { + int r = ::write (fnum, bytes, len); + if (r == -1) + { + if (java::lang::Thread::interrupted()) + { + java::io::InterruptedIOException *iioe + = new java::io::InterruptedIOException + (JvNewStringLatin1 (strerror (errno))); + iioe->bytesTransferred = written; + throw iioe; + } + // Some errors should not cause exceptions. + if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF) + throw new java::io::IOException (JvNewStringUTF (strerror (errno))); + } + written += r; + len -= r; + bytes += r; + } +} + + +// Read a single byte from the socket. +jint +java::net::PlainSocketImpl::read(void) +{ + jbyte b; + + // Do timeouts via select. + if (timeout > 0) + { + // Create the file descriptor set. + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (fnum,&read_fds); + // Create the timeout struct based on our internal timeout value. + struct timeval timeout_value; + timeout_value.tv_sec = timeout / 1000; + timeout_value.tv_usec = (timeout % 1000) * 1000; + // Select on the fds. + int sel_retval = _Jv_select (fnum + 1, &read_fds, NULL, NULL, &timeout_value); + // If select returns 0 we've waited without getting data... + // that means we've timed out. + if (sel_retval == 0) + throw new java::io::InterruptedIOException + (JvNewStringUTF ("read timed out") ); + // If select returns ok we know we either got signalled or read some data... + // either way we need to try to read. + } + int r = ::read (fnum, &b, 1); + + if (r == 0) + return -1; + if (java::lang::Thread::interrupted()) + { + java::io::InterruptedIOException *iioe = + new java::io::InterruptedIOException + (JvNewStringUTF("read interrupted")); + iioe->bytesTransferred = r == -1 ? 0 : r; + throw iioe; + } + else if (r == -1) + { + // Some errors cause us to return end of stream... + if (errno == ENOTCONN) + return -1; + // Other errors need to be signalled. + throw new java::io::IOException (JvNewStringUTF (strerror (errno))); + } + return b & 0xFF; +} + +// Read count bytes into the buffer, starting at offset. +jint +java::net::PlainSocketImpl::read(jbyteArray buffer, jint offset, jint count) +{ + if (! buffer) + throw new java::lang::NullPointerException; + jsize bsize = JvGetArrayLength (buffer); + if (offset < 0 || count < 0 || offset + count > bsize) + throw new java::lang::ArrayIndexOutOfBoundsException; + jbyte *bytes = elements (buffer) + offset; + + // Do timeouts via select. + if (timeout > 0) + { + // Create the file descriptor set. + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (fnum, &read_fds); + // Create the timeout struct based on our internal timeout value. + struct timeval timeout_value; + timeout_value.tv_sec = timeout / 1000; + timeout_value.tv_usec =(timeout % 1000) * 1000; + // Select on the fds. + int sel_retval = _Jv_select (fnum + 1, &read_fds, NULL, NULL, &timeout_value); + // We're only interested in the 0 return. + // error returns still require us to try to read + // the socket to see what happened. + if (sel_retval == 0) + { + java::io::InterruptedIOException *iioe = + new java::io::InterruptedIOException + (JvNewStringUTF ("read interrupted")); + iioe->bytesTransferred = 0; + throw iioe; + } + } + // Read the socket. + int r = ::recv (fnum, bytes, count, 0); + if (r == 0) + return -1; + if (java::lang::Thread::interrupted()) + { + java::io::InterruptedIOException *iioe = + new java::io::InterruptedIOException + (JvNewStringUTF ("read interrupted")); + iioe->bytesTransferred = r == -1 ? 0 : r; + throw iioe; + } + else if (r == -1) + { + // Some errors cause us to return end of stream... + if (errno == ENOTCONN) + return -1; + // Other errors need to be signalled. + throw new java::io::IOException (JvNewStringUTF (strerror (errno))); + } + return r; +} + +// How many bytes are available? +jint +java::net::PlainSocketImpl::available(void) +{ +#if defined(FIONREAD) || defined(HAVE_SELECT) + long num = 0; + int r = 0; + bool num_set = false; + +#if defined(FIONREAD) + r = ::ioctl (fnum, FIONREAD, &num); + if (r == -1 && errno == ENOTTY) + { + // If the ioctl doesn't work, we don't care. + r = 0; + num = 0; + } + else + num_set = true; +#elif defined(HAVE_SELECT) + if (fnum < 0) + { + errno = EBADF; + r = -1; + } +#endif + + if (r == -1) + { + posix_error: + throw new java::io::IOException(JvNewStringUTF(strerror(errno))); + + } + + // If we didn't get anything we can use select. + +#if defined(HAVE_SELECT) + if (! num_set) + { + fd_set rd; + FD_ZERO (&rd); + FD_SET (fnum, &rd); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + r = _Jv_select (fnum + 1, &rd, NULL, NULL, &tv); + if(r == -1) + goto posix_error; + num = r == 0 ? 0 : 1; + } +#endif /* HAVE_SELECT */ + + return (jint) num; +#else + throw new java::io::IOException (JvNewStringUTF ("unimplemented")); +#endif + } + + void java::net::PlainSocketImpl::setOption (jint optID, java::lang::Object *value) { -- 2.7.4