一.服务端多任务处理
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()方法调用返回前,必须把一些数据成功传输到连接的另一端.