Soketサービス側マルチタスクとブロックタイムアウト

7336 ワード

一.サービス側マルチタスク処理
1.まずExecutorインスタンスを作成する.承認された各クライアントSocketをタスクとしてExecutorに送信して実行します.
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
public class TCPEchoServerExecutor {
    public static void main(String[] args) throws IOException {
        // Create a server socket to accept client connection requests
        ServerSocket servSock = new ServerSocket(9000);
        Logger logger = Logger.getLogger("practical");
        Executor service = Executors.newCachedThreadPool();  
        // Run forever, accepting and spawning threads to service each connection
        while (true) {
            Socket clntSock = servSock.accept(); // Block waiting for connection
            service.execute(new TimeLimitEchoProtocol(clntSock, logger));
        }
    /* NOT REACHED */
    }
}

2.Executorに実行を任せるので、もちろんRunnableインタフェースを実現する
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TimeLimitEchoProtocol implements Runnable {
    private static final int BUFSIZE = 32;  // Size (bytes) buffer
    private static final String TIMELIMIT = "10000";  // Default limit (ms)
    private static final String TIMELIMITPROP = "Timelimit";  // Thread property

    private static int timelimit;
    private Socket clntSock;
    private Logger logger;

    public TimeLimitEchoProtocol(Socket clntSock, Logger logger) {
        this.clntSock = clntSock;
        this.logger = logger;
        // Get the time limit from the System properties or take the default
        timelimit = Integer.parseInt(System.getProperty(TIMELIMITPROP, TIMELIMIT));
    }

    public static void handleEchoClient(Socket clntSock, Logger logger) {//           

        try {
            // Get the input and output I/O streams from socket
            InputStream in = clntSock.getInputStream();
            OutputStream out = clntSock.getOutputStream();
            int recvMsgSize;                        // Size of received message
            int totalBytesEchoed = 0;               // Bytes received from client
            byte[] echoBuffer = new byte[BUFSIZE];  // Receive buffer
            long endTime = System.currentTimeMillis() + timelimit;
            int timeBoundMillis = timelimit;

            clntSock.setSoTimeout(timeBoundMillis);//           
            // Receive until client closes connection, indicated by -1
            while ((timeBoundMillis > 0) &&     // catch zero values
                    ((recvMsgSize = in.read(echoBuffer)) != -1)) {
                out.write(echoBuffer, 0, recvMsgSize);
                totalBytesEchoed += recvMsgSize;
                timeBoundMillis = (int) (endTime - System.currentTimeMillis());
                clntSock.setSoTimeout(timeBoundMillis);
            }
            logger.info("Client " + clntSock.getRemoteSocketAddress() +
                    ", echoed " + totalBytesEchoed + " bytes.");
        } catch (IOException ex) {
            logger.log(Level.WARNING, "Exception in echo protocol", ex);
        }
    }

    public void run() {
        handleEchoClient(this.clntSock, this.logger);
    }
}

二.ブロックとタイムアウト処理
SocketのI/O呼び出しは、様々な理由でブロックされる場合があります.データ入力方法read()とreceive()は、データ読み取りがない場合にブロックする.Tcp socketのwrite()メソッドは、転送するデータを十分な空間キャッシュがない場合にブロックする可能性がある.ServerSocketのaccept()とSocketのコンストラクション関数は、接続が確立するまでブロックされます.accept()、read()、receive():Socket、ServerSocket、DatagramSocketのsetSoTimeout()メソッドを使用できます.Socketインスタンスでは、read()メソッドを呼び出す前に、SocketのInputStreamのavailable()メソッドを使用してconnect()を検出したり、リモート接続のタイムアウト時間を指定したりすることができます.write()も、最後のバイトがTcp実装のローカルキャッシュに正常に書き込まれるまでブロックする.利用可能なキャッシュスペースが書き込みデータよりも小さい場合、write()メソッド呼び出しが戻る前に、接続の反対側にデータの一部を正常に転送する必要がある.