ページ更新: 2009-10-20 (火) (3288日前)

(2004-09-05 新規作成)

説明用に即席で使ったコード。手抜き多し。せっかくなので貼っておく。

目次

[編集]

UDP: DatagramPacket, DatagramSocket (2005-04-06, 2009-10-20) #

  • UDPのサンプルコード。
  • 送信データ(Data)を作成して、byte[]に変換 (ByteBufferを用いる)、送信(DatagramPacket, DatagramSocket) する。
  • サーバ(Server)で受信(DatagramSocket, DatagramPacket)して、データ(Data)に変換 (ByteBuffer)して、表示。
  • 2009-10-20 バグ修正 (String#length() をString#getBytes()#length と同一視していた)、コードを整理 (Dataクラスをimmutableに、他)、コメントを追加
[編集]

ソースコード #

// -*-mode:java; coding:utf-8;-*-

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public final class UdpSocketTrial {

    private static final int MAX_PACKETS = 10;

    private static final int PORT = 9999;

    private static final byte[] ADDRESS = { (byte) 127, (byte) 0, (byte) 0, (byte) 1, };

    public static void main(final String[] args) {

        try {
            // サーバーを起動する
            new Thread(new Server(ADDRESS, PORT, MAX_PACKETS)).start();

            // サーバーにパケットをmaxPackets個送信する
            final InetAddress host = InetAddress.getByAddress(ADDRESS);
            final DatagramSocket socket = new DatagramSocket();

            for (int i = 0; i < MAX_PACKETS; i++) {
                System.out.println("Client: send data " + i);
                System.out.flush();

                final Data data = new Data(i, "No. " + Integer.toString(i) + ", string data.");
                final byte[] buf = data.toByteArray();

                socket.send(new DatagramPacket(buf, 0, buf.length, host, PORT));
            }
        } catch (final UnknownHostException e) {
            e.printStackTrace();

        } catch (final SocketException e) {
            e.printStackTrace();

        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Receive UDP Socket
     * パケットを受信し、デコードして、表示する。
     * 所定に個数のパケット {@link maxPackets} を受信すると、サーバを終了する。
     */
    private static final class Server implements Runnable {
        private final int maxPackets;

        private final DatagramSocket socket;

        private Server(final byte[] address, final int port, final int maxPackets)
                throws UnknownHostException, SocketException {

            this.maxPackets = maxPackets;

            final InetAddress localHost = InetAddress.getByAddress(address);
            socket = new DatagramSocket(port, localHost);
            socket.setReuseAddress(true);
            socket.setReceiveBufferSize(8192);
            socket.setSendBufferSize(8192);
            socket.setSoTimeout(100 * 1024);
            socket.setBroadcast(false);
        }

        public void run() {
            try {
                doServer();
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * パケットを受信し、デコードして、表示する。
         * 所定に個数のパケット {@link maxPackets} を受信すると、サーバを終了する。
         * @throws IOException
         */
        private void doServer() throws IOException {
            final byte[] buf = new byte[8192];
            final DatagramPacket received = new DatagramPacket(buf, buf.length);
            received.setLength(buf.length);

            System.out.println("Server: start");
            System.out.flush();

            for (int nPackets = 0; nPackets < maxPackets; nPackets++) {
                socket.receive(received);

                final Data data = Data.fromByteArray(received.getData());

                System.out.println("Server: nPackets=" + nPackets + " data=" + data);
                System.out.flush();
            }

            System.out.println("Server: end");
            System.out.flush();
        }
    }

    /**
     * パケットとして送受信するデータ。
     *
     * データフォーマット
     * <pre>
     * | id (int) | string-len (int) | string |
     * </pre>
     */
    private static final class Data {
        private final int id;

        private final String string;

        private Data(final int id, final String string) {
            this.id = id;
            this.string = string;
        }

        private static Data fromByteArray(final byte[] array) {
            final ByteBuffer buf = ByteBuffer.wrap(array);
            buf.order(ByteOrder.BIG_ENDIAN);

            final int id = buf.getInt();
            final int len = buf.getInt();
            final byte[] str = new byte[len];
            buf.get(str, 0, len);
            final String string = new String(str);

            return new Data(id, string);
        }

        private byte[] toByteArray() {
            final ByteBuffer buf = ByteBuffer.allocate(8192);
            buf.order(ByteOrder.BIG_ENDIAN);

            buf.putInt(id);
            final byte[] str = string.getBytes();
            buf.putInt(str.length);
            buf.put(str);

            final byte[] result = new byte[buf.position()];
            System.arraycopy(buf.array(), 0, result, 0, result.length);

            return result;
        }

        public String toString() {
            return "id=" + id + " string='" + string + "'";
        }
    }
}
[編集]

実行結果 #

Server: start
Client: send data 0
Client: send data 1
Client: send data 2
Client: send data 3
Client: send data 4
Server: nPackets=0 data=id=0 string='No. 0, string data.'
Client: send data 5
Server: nPackets=1 data=id=1 string='No. 1, string data.'
Client: send data 6
Server: nPackets=2 data=id=2 string='No. 2, string data.'
Client: send data 7
Server: nPackets=3 data=id=3 string='No. 3, string data.'
Client: send data 8
Server: nPackets=4 data=id=4 string='No. 4, string data.'
Client: send data 9
Server: nPackets=5 data=id=5 string='No. 5, string data.'
Server: nPackets=6 data=id=6 string='No. 6, string data.'
Server: nPackets=7 data=id=7 string='No. 7, string data.'
Server: nPackets=8 data=id=8 string='No. 8, string data.'
Server: nPackets=9 data=id=9 string='No. 9, string data.'
Server: end
[編集]

TCP: Socket, ServerSocket, 例外 (2004-09-05, 2009-10-20) #

  • JDK 1.1 の新しい SocketExceptionsで追加された3つの例外を起こしてみる。
    • UnknownHostExceptionを起こしてみる。
    • SocketTimeoutException を起こしてみる。
    • ConnectException を起こしてみる。
  • サーバを起動し、データを作成 (Data) して、DataOutputStreamを使ってクライアントに送信する。
  • クライアントで、ソケットからストリームを得て、DataInputStreamを使ってデータ(Data)に変換して、表示する。
  • 接続中にSocketTimeoutException を起こしてみる。
  • サーバでソケットを片方ずつ閉じて (shutdownInput, shutdownOutput)、最後にclose する。
[編集]

ソースコード #

  • 2009-10-20 バグ修正 (String#length() をString#getBytes()#length と同一視していた)、Read time outも確認できるよう修正、データの受信処理を追加、finalを追加、コメントを追加、実行環境を Java SE 6 に変更
// -*-mode:java; coding:utf-8;-*-

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public final class SocketTrial {

    private static final int CONNECT_TIMEOUT = 2 * 1000;

    private static final int CONNECTION_TIMEOUT = 1 * 1000;

    private static final String UNKNOWN_HOST = "unknown.example.jp";

    private static final String NON_EXIST_HOST = "192.168.1.99";

    private static final String LOCALHOST = "localhost";

    private static final int PORT = 40000;

    private static final int BACKLOG = 1;

    public static void main(final String[] args) {
        final StopWatch watch = new StopWatch();

        // 逆引きできないホストに接続しようとしたときの例外を調べる
        try {
            watch.start();
            tryConnect(UNKNOWN_HOST, PORT);

        } catch (final IOException e) {
            e.printStackTrace(System.out);
        }
        System.out.println("Client: duration (msec)=" + watch.end());
        System.out.println();

        // 同じサブネットだが、存在しないホストに接続しようとしたときの例外を調べる
        try {
            watch.start();
            tryConnect(NON_EXIST_HOST, PORT);

        } catch (final IOException e) {
            e.printStackTrace(System.out);
        }
        System.out.println("Client: duration (msec)=" + watch.end());
        System.out.println();

        // 同じホストだが、サーバプロセスがないときの例外を調べる
        try {
            watch.start();
            tryConnect(LOCALHOST, PORT);

        } catch (final IOException e) {
            e.printStackTrace(System.out);
        }
        System.out.println("Client: duration (msec)=" + watch.end());
        System.out.println();

        // 同じホストで、サーバプロセスもあるときに、通信タイムアウトの例外を調べる
        try {
            final SampleServer sampleServer = new SampleServer(PORT, BACKLOG);
            new Thread(sampleServer).start();

            watch.start();
            tryConnect(LOCALHOST, PORT);

        } catch (final IOException e) {
            e.printStackTrace(System.out);
        }
        System.out.println("Client: duration (msec)=" + watch.end());
        System.out.println();
    }

    private static void tryConnect(final String hostname, final int port)
    throws IOException {
        final long startTimeMillis = System.currentTimeMillis();

        System.out.println("Client: enter tryConnect (hostname=" + hostname + ", port=" + port
                + ")");

        // ソケットを作成する
        final Socket socket = new Socket();
        socket.setSoTimeout(CONNECTION_TIMEOUT);

        // 接続を試みる
        final InetSocketAddress endpoint = new InetSocketAddress(
                InetAddress.getByName(hostname), port);
        socket.connect(endpoint, CONNECT_TIMEOUT);
        System.out.println("Client: connect! socket=" + socket.getLocalSocketAddress());
        System.out.println("Client: leave tryConnect");

        // ソケットで受信したデータを読み込んで表示
        final BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
        for (;;) {
            final Data data = Data.readStream(in);
            System.out.println("Client: receive data:" + data);
        }
    }

    private static final class StopWatch {
        private long startTimeMillis = 0;
        public void start() {
            startTimeMillis = System.currentTimeMillis();
        }
        public long end() {
            try {
                return System.currentTimeMillis() - startTimeMillis;
            } finally {
                startTimeMillis = 0;
            }
        }
    }

    private static final class SampleServer implements Runnable {

        private final int port;
        private final int backlog;

        private SampleServer(final int port, final int backlog) {
            this.port = port;
            this.backlog = backlog;
        }

        public void run() {
            try {
                doServer();

            } catch (final IOException e) {
                e.printStackTrace(System.out);

            } catch (final Throwable e) {
                e.printStackTrace(System.out);
            }
        }

        /**
         * サーバを起動する。
         * このサーバはクライアントからの接続を待ち、shutdownInput(), shutdownOutput(), close() を
         * @throws IOException
         */
        private void doServer() throws IOException {
            final ServerSocket server = new ServerSocket(port, backlog);
            System.out.println("Server: start, listen port=" + port);

            // クライアントからの接続を待つ
            final Socket client = server.accept();

            // 接続されたら、クライアントのソケットアドレスを表示する
            System.out.println("Server: connected from client="
                    + client.getRemoteSocketAddress());

            // データを10個、クライアントに送る。
            final BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
            try {
                for (int i = 0; i < 10; i++) {
                    final Data data = new Data(i, "test data no." + i);
                    data.writeStream(out);
                }
            } finally {
                out.flush();
            }

            // 2秒まち、shutdownInputする。
            sleep(2 * 1000);
            client.shutdownInput();
            System.out.println("Server: shutdownInput");

            // 2秒待ち、shutdownOutputする。
            sleep(2 * 1000);
            client.shutdownOutput();
            System.out.println("Server: shutdownOutput");

            // 2秒待ち、closeする。
            sleep(2 * 1000);
            client.close();
            System.out.println("Server: close");
        }

        private static void sleep(final long millisec) {
            try {
                Thread.sleep(millisec);
            } catch (final InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * パケットとして送受信するデータ。
     *
     * データフォーマット
     * <pre>
     * | id (int) | string-len (int) | string |
     * </pre>
     *
     * intはbig-endianで。
     */
    private static final class Data {
        private final int id;

        private final String string;

        private Data(final int id, final String string) {
            this.id = id;
            this.string = string;
        }

        private static Data readStream(final InputStream in) throws IOException {
            final DataInputStream dis = new DataInputStream(in);

            final int id = dis.readInt();
            final int len = dis.readInt();
            final byte[] str = new byte[len];
            dis.read(str);
            final String string = new String(str);

            return new Data(id, string);
        }

        private void writeStream(final OutputStream out) throws IOException {
            final DataOutputStream dos = new DataOutputStream(out);
            try {
                dos.writeInt(id);
                final byte[] str = string.getBytes();
                dos.writeInt(str.length);
                dos.write(str);

            } finally {
                dos.flush();
            }
        }

        public String toString() {
            return "id=" + id + " string='" + string + "'";
        }
    }
}
[編集]

実行結果 #

以下の環境を使用。OSはWindows XP Professional SP3。

C:> java -version
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)

実行結果:

Client: enter tryConnect (hostname=unknown.example.jp, port=40000)
java.net.UnknownHostException: unknown.example.jp
	at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
	at java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:849)
	at java.net.InetAddress.getAddressFromNameService(InetAddress.java:1200)
	at java.net.InetAddress.getAllByName0(InetAddress.java:1153)
	at java.net.InetAddress.getAllByName(InetAddress.java:1083)
	at java.net.InetAddress.getAllByName(InetAddress.java:1019)
	at java.net.InetAddress.getByName(InetAddress.java:969)
	at SocketTrial.tryConnect(SocketTrial.java:95)
	at SocketTrial.main(SocketTrial.java:37)
Client: duration (msec)=15

Client: enter tryConnect (hostname=192.168.1.99, port=40000)
java.net.SocketTimeoutException: connect timed out
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
	at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
	at java.net.Socket.connect(Socket.java:525)
	at SocketTrial.tryConnect(SocketTrial.java:96)
	at SocketTrial.main(SocketTrial.java:48)
Client: duration (msec)=2016

Client: enter tryConnect (hostname=localhost, port=40000)
java.net.ConnectException: Connection refused: connect
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
	at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
	at java.net.Socket.connect(Socket.java:525)
	at SocketTrial.tryConnect(SocketTrial.java:96)
	at SocketTrial.main(SocketTrial.java:59)
Client: duration (msec)=1062

Client: enter tryConnect (hostname=localhost, port=40000)
Server: start, listen port=40000
Server: connected from client=/127.0.0.1:2197
Client: connect! socket=/127.0.0.1:2197
Client: leave tryConnect
Client: receive data:id=0 string='test data no.0'
Client: receive data:id=1 string='test data no.1'
Client: receive data:id=2 string='test data no.2'
Client: receive data:id=3 string='test data no.3'
Client: receive data:id=4 string='test data no.4'
Client: receive data:id=5 string='test data no.5'
Client: receive data:id=6 string='test data no.6'
Client: receive data:id=7 string='test data no.7'
Client: receive data:id=8 string='test data no.8'
Client: receive data:id=9 string='test data no.9'
java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:129)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:237)
	at java.io.DataInputStream.readInt(DataInputStream.java:370)
	at SocketTrial$Data.readStream(SocketTrial.java:219)
	at SocketTrial$Data.access$2(SocketTrial.java:216)
	at SocketTrial.tryConnect(SocketTrial.java:103)
	at SocketTrial.main(SocketTrial.java:73)
Client: duration (msec)=1500

Server: shutdownInput
Server: shutdownOutput
Server: close