natPlainSocketImpl.cc: Added timeout handling for sockets.
2002-01-08 Nic Ferrier <nferrier@tf1.tapsellferrier.co.uk> * 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
This commit is contained in:
parent
14b3e8ef09
commit
2b521fa72e
3 changed files with 381 additions and 15 deletions
|
@ -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 <bothner@cygnus.com>
|
||||
* @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 <bothner@cygnus.com>
|
||||
* @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
|
||||
*/
|
||||
|
||||
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:
|
||||
* <pre>
|
||||
* fd = new FileDescriptor (fnum);
|
||||
* </pre>
|
||||
*
|
||||
* 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 <nferrier@tapsellferrier.co.uk>
|
||||
*/
|
||||
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 <nferrier@tapsellferrier.co.uk>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <java/lang/Boolean.h>
|
||||
#include <java/lang/Class.h>
|
||||
#include <java/lang/Integer.h>
|
||||
#include <java/lang/Thread.h>
|
||||
#include <java/lang/NullPointerException.h>
|
||||
#include <java/lang/ArrayIndexOutOfBoundsException.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue