1/*
2 * Copyright (c) 2011 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.jme3.network.kernel.tcp;
34
35import com.jme3.network.kernel.Connector;
36import com.jme3.network.kernel.ConnectorException;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.net.InetAddress;
41import java.net.Socket;
42import java.net.SocketAddress;
43import java.nio.ByteBuffer;
44import java.util.concurrent.atomic.AtomicBoolean;
45
46
47/**
48 *  A straight forward socket-based connector implementation that
49 *  does not use any separate threading.  It relies completely on
50 *  the buffering in the OS network layer.
51 *
52 *  @version   $Revision: 8843 $
53 *  @author    Paul Speed
54 */
55public class SocketConnector implements Connector
56{
57    private Socket sock;
58    private InputStream in;
59    private OutputStream out;
60    private SocketAddress remoteAddress;
61    private byte[] buffer = new byte[65535];
62    private AtomicBoolean connected = new AtomicBoolean(false);
63
64    public SocketConnector( InetAddress address, int port ) throws IOException
65    {
66        this.sock = new Socket(address, port);
67        remoteAddress = sock.getRemoteSocketAddress(); // for info purposes
68
69        // Disable Nagle's buffering so data goes out when we
70        // put it there.
71        sock.setTcpNoDelay(true);
72
73        in = sock.getInputStream();
74        out = sock.getOutputStream();
75
76        connected.set(true);
77    }
78
79    protected void checkClosed()
80    {
81        if( sock == null )
82            throw new ConnectorException( "Connection is closed:" + remoteAddress );
83    }
84
85    public boolean isConnected()
86    {
87        if( sock == null )
88            return false;
89        return sock.isConnected();
90    }
91
92    public void close()
93    {
94        checkClosed();
95        try {
96            Socket temp = sock;
97            sock = null;
98            connected.set(false);
99            temp.close();
100        } catch( IOException e ) {
101            throw new ConnectorException( "Error closing socket for:" + remoteAddress, e );
102        }
103    }
104
105    public boolean available()
106    {
107        checkClosed();
108        try {
109            return in.available() > 0;
110        } catch( IOException e ) {
111            throw new ConnectorException( "Error retrieving data availability for:" + remoteAddress, e );
112        }
113    }
114
115    public ByteBuffer read()
116    {
117        checkClosed();
118
119        try {
120            // Read what we can
121            int count = in.read(buffer);
122            if( count < 0 ) {
123                // Socket is closed
124                close();
125                return null;
126            }
127
128            // Wrap it in a ByteBuffer for the caller
129            return ByteBuffer.wrap( buffer, 0, count );
130        } catch( IOException e ) {
131            if( !connected.get() ) {
132                // Nothing to see here... just move along
133                return null;
134            }
135            throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e );
136        }
137    }
138
139    public void write( ByteBuffer data )
140    {
141        checkClosed();
142
143        try {
144            out.write(data.array(), data.position(), data.remaining());
145        } catch( IOException e ) {
146            throw new ConnectorException( "Error writing to connection:" + remoteAddress, e );
147        }
148    }
149
150}
151