import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TCPServer implements Runnable{
/*绑定监听IP:port
* 1、起一个主线程进行监听,采用 thread per request的方式处理请求;
* 2、每个请求,回送一个应答*/
public static final int MAX_THREAD = 10;
private String ip = null;
private int port = 0;
private SocketAddress listeningAddress = null;
private ServerSocket serverSocket = null;
private Logger logger = Logger.getLogger(this.getClass().getName()); //Use default logger of JAVA
private Socket connSocket = null;
private ExecutorService threadpool = Executors.newFixedThreadPool(MAX_THREAD);
private Map<String,ConnectionInfo> connectionPool = Collections.synchronizedMap(new HashMap<String,ConnectionInfo>());
public volatile boolean isRunning = true;
public TCPServer(String ip,int port) {
this.ip = ip;
this.port = port;
listeningAddress = new InetSocketAddress(ip, port);
try {
serverSocket = new ServerSocket(port, 0, ((InetSocketAddress)listeningAddress).getAddress());
logger.log(Level.INFO, "Server started...listening on "+this.ip+":"+this.port);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while( true == isRunning )
{
try {
connSocket = serverSocket.accept();
if( null != connSocket )
{
//handle connect in newly constructed thread
Future<?> future = threadpool.submit(new RequestHandler(connSocket));
String connectionKey = new String(connSocket.getInetAddress().getHostAddress()+":"+connSocket.getPort());
connectionPool.put(connectionKey, new ConnectionInfo(future,connSocket)); /*讲连接放入server的连接池,便于后续使用*/
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ConnectionInfo {
public Future<?> future = null;
public Socket socket = null;
public ConnectionInfo(Future<?> future,Socket socket) {
this.future = future;
this.socket = socket;
}
}
private class RequestHandler implements Runnable {
private final int RECV_BUF_LEN = 1024;
private Socket connSocket = null;
private InputStream in = null;
private OutputStream out = null;
private BufferedInputStream bufin = null;
private BufferedOutputStream bufout = null;
private boolean isRunning = true;
private byte[] recvbuffer = new byte[RECV_BUF_LEN];
private int handleswitch = 2;
//recvbuffer := make([]byte,1024,1024) /*define a slice in golang*/
//var recvbuffer [1024] byte /*define an array in golang*/
public RequestHandler(Socket connSocket) {
this.connSocket = connSocket;
}
@Override
public void run() {
if( null != connSocket ) {
try {
in = connSocket.getInputStream();
out = connSocket.getOutputStream();
bufin = new BufferedInputStream(connSocket.getInputStream());
//bufout = new BufferedOutputStream(connSocket.getOutputStream(),200);
/*如果创建的时候不指定buffer大小,默认是8K;如果buffer较小,buffer满的时候会自动执行flush;stream关闭的时候,会自动flush*/
bufout = new BufferedOutputStream(connSocket.getOutputStream());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// TODO Auto-generated method stub
//get info from the socket via stream
while( !Thread.currentThread().isInterrupted() && true == isRunning) {
if( null != connSocket ) {
if( handleswitch == 1 ) {
//第一种方式,通过传统的stream方式读取
if( null != in && null != out)
{
// in.read(); /*返回stream中的下一个byte,作为int类型返回,如果stream到达末尾,则返回-1*/
try {
int cnt = in.read(recvbuffer);
if( cnt > 0) {
//recv success
String received = new String(recvbuffer,0,cnt); /*注意使用有效的数据长度,而不是整个buffer的长度创建string,避免乱码*/
//logger.log(Level.INFO, "Received: "+received);
//send an echo as soon as possible
String response = new String("I'm the server,received your message:"+received);
logger.log(Level.INFO, response);
out.write(response.getBytes());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//send an event to TCPSever to notify it to handle connection failure.
isRunning = false;
}
}
} else if( handleswitch == 2 ) {
//第二种方式,使用bufferedstream的方式读取
if( bufin != null && bufout != null ) {
try {
int cnt = bufin.read(recvbuffer); //底层是调用InputStream的read逐个字节读到buffer里的
if( cnt > 0 ) {
String received = new String(recvbuffer,0,cnt); /*注意使用有效的数据长度,而不是整个buffer的长度创建string,避免乱码*/
//logger.log(Level.INFO, received);
//send an echo as soon as possible
String response = new String("I'm the server,received your message:"+received);
logger.log(Level.INFO, response);
bufout.write(response.getBytes());
bufout.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
try {
connSocket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
isRunning = false;
}
}
/*综上:个人认为,bufferedstream的意义,在发送的时候大于接收的时候,从源码看到,接收的时候,基本还是调用底层的read逐个字节读的;
* 在发送端,业务线程可以不断把数据往buffer里填,直到需要发送或者buffer满的时候,调用或触发flush将数据提交给OS(并非提交给
* 物理设备,如网卡),这样在某些场景下,业务线程不必每填一次数据都write一把,提高了效率。*/
} else if( handleswitch == 3 ) {
//第三种方式,使用reader/writer方式读取
try {
//从字节流到字符流再到文本
BufferedReader br = new BufferedReader(new InputStreamReader(connSocket.getInputStream(), "UTF-8"));
if( null != br ) {
String line = null;
//BufferedReader 有几种读的方式:1、逐个字符读取;2、将字符流顺序读取到指定的缓存中;3、逐行读取(以换行符为准)
while( null != ( line = br.readLine()) ) {
//readline 以换行符、字符串结束符为准
//如果读不到行结束,该调用会阻塞并一直读取,直到字符流结束,内部用StringBuffer保存每次读取的string
logger.log(Level.INFO, "Server received:"+line);
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
logger.log(Level.INFO, "Socket is null! Stop reading!");
isRunning = false;
}
}
}
}
}
【代码积累】TCP server
最新推荐文章于 2024-05-22 10:00:35 发布