您的位置:首页 > 理论基础 > 计算机网络

java 网络编程之ServerSocket详解

2015-10-16 12:19 651 查看
构造ServerSocket :

在客户/服务器通信模式中,服务器需要创建创建监听特定端口的ServerSocket。负责接收客户连接请求。

如果在构造方法运行过程中 如果无法绑定到指定的端口上,会抛出BindException。

有如下原因:

1:端口号被占用。

2 :在某些OS中,如果没有以超级用户身份运行程序,不允许绑定到0-1023的端口。

设定客户连接请求的队列:

当服务器进程运行时,可能会同时监听多个客户的连接请求。SerVerSocket构造方法的backlog参数用来设置连接请求队列的长度。

ServerSocket的设置:

SO_TIMEOUT:表示等待客户连接的超时时间。

SO_Rcvbuf :表示接收数据缓冲区的大小。

SO_REUSEADDR: 表示是否重用服务器所绑定的端口号。

创建多线程的服务器:

可以用并发性能来衡量一个服务器同时响应多个客户的能力。一个具有好的并发性能的服务器,必须符合两个条件:

能同时接受并处理多个客户连接

对于每个客户,都会迅速给予响应。

创建具有多线程的服务器的方式:

1:为每一个客户分配一个工作线程。

2:创建一个线程池由其中的工作线程为客户服务。

3:利用idk的java类库中现成的线程池,由它的工作线程来为客户服务。

关闭服务器:

具有关闭自己功能的服务器除了监听普通客户程序的连接外,还会在其它端口监听管理程序AdminClient的连接,当服务器在8001 端口接收到AdminClient发送的“shutDown”命令时,就会开始关闭服务器,不会在接收任何客户端发送的进程连接请求,对于那些已经接收但还没有处理的客户连接,则会丢弃与该客户的通信任务,而不会吧通信任务加入到线程池的工作队列中,服务器会等待线程池把当前工作队列中的所有任务执行完成,才结束任务。

代码如下

public class EchoService {

private int port = 8000;// 服务端口号

private ServerSocket serverSocket;

private ExecutorService executorService; //线程池

private final int POOL_SIZE = 4 ; //单个CPU时线程池中工作线程的数目

private int portForShutDown = 8001; // 用于监听关闭服务器的端口

private ServerSocket serverSocketForShutdown;

private boolean isShutdown=false; //标志服务器是否关闭

private Thread shutdownThread = new Thread() { // 负责关闭服务器的线程

public void start() {

this.setDaemon(true); //设置为守护线程(也称为后台线程)

super.start();

}

public void run () {

while(!isShutdown) {

Socket socketForShutdown = null;

try {

socketForShutdown= serverSocketForShutdown .accept();

BufferedReader br = new BufferedReader(new InputStreamReader(socketForShutdown.getInputStream()));

String command = br.readLine();

if (command .equals("shutdown")) {

long beginTime = System.currentTimeMillis();

socketForShutdown.getOutputStream().write("服务器正在关闭\r\n".getBytes());

isShutdown= true;

//请求关闭线程池,线程池不会接受新的任务,但是会继续执行工作队列中已有的任务

executorService.shutdown();

while (!executorService.isTerminated())

executorService.awaitTermination(30, TimeUnit.SECONDS);//等待关闭线程池,每次等待的超时时间为30秒

serverSocket.close();//关闭与客户端通信的ServerSocket

long endTime = System.currentTimeMillis();

socketForShutdown.getOutputStream().write(("服务器已经关闭"+"关闭服务器使用了"+(endTime-beginTime)+"毫秒\r\n").getBytes() );

socketForShutdown.close();

serverSocket.close();

}else {

socketForShutdown.getOutputStream().write("错误的命令".getBytes());

socketForShutdown.close();

}

}catch (Exception e) {

e.printStackTrace();

}

}

}

};

public EchoService() throws Exception{

serverSocket = new ServerSocket(port);

serverSocket.setSoTimeout(600000);//设定等待客户连接的超时时间为60秒

serverSocketForShutdown = new ServerSocket(portForShutDown);

//创建线程池

executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*POOL_SIZE);

shutdownThread.start();//启动关闭服务器的线程

System.out.println("服务器启动中");

}

public void service () {

while (!isShutdown) {

Socket socket = null;

try {

socket = serverSocket.accept();

socket.setSoTimeout(60000);//等待客户发送数据超时时间设定为60秒

executorService.execute(new Handle ( socket));

} catch (SocketTimeoutException e) {

//不必处理等待客户连接时出现超时异常

} catch (RejectedExecutionException e) {

try {

if (socket!= null) {

socket.close();

}

}catch(IOException x) {

} return;

}catch (SocketException e) {

if (e.getMessage().indexOf("socket closed")!=-1) return;

}catch (IOException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) throws Exception {

new EchoService().service();

}

class Handle implements Runnable{

Socket s = null;

public Handle (Socket s) {

this.s=s;

}

public void run() {

try {

BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

PrintWriter pw = new PrintWriter(new BufferedOutputStream(s.getOutputStream()));

boolean flag = true;

while (flag) {

String info = br.readLine();

if ("".equals(info) || "bye".equals(info)) {

flag = false;

}else {

System.out.println(info);

pw.println("echo:"+info);

pw.flush();//保证缓冲区的数据都输出去

}

}

br.close();

pw.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: