服务器Socket概述与实例
2017-09-13 09:12
591 查看
绝大部分知识与实例来自O’REILLY的《Java网络编程》(Java Network Programming,Fourth Edition,by Elliotte Rusty Harold(O’REILLY))。
在Java中,一个服务器的生命周期如下:
使用ServerSocket的构造函数在一个特定端口创建一个新的ServerSocket;
ServerSocket使用其accept()方法监听这个端口的入站连接。accept()方法会一直阻塞,直到一个客户端尝试建立连接,此时方法会返回一个连接客户端和服务器的Socket对象;
根据服务器的类型调用Socket的getInputStream()和getOutputStream()获取输入/输出流;
服务器和客户端根据协议交互,直到关闭连接;
服务器或客户端关闭连接;
服务器回到步骤2,等待下一次连接。
下面看一个简单的服务器,用于获取时间:
启动服务器:
请求服务器提供服务:
注意点有以下几个:
客户端Socket与服务器Socket使用完毕后都需要调用close()关闭连接,Java 7及之后的版本可以利用try-with-resources实现。
启动服务器的代码和请求服务器提供服务的代码要分开执行,否则可能得不到输出。
服务器的代码中有两类异常,一类是某个客户端连接出错时抛出的,一类是服务器出错时抛出的,必须分别处理。
在同一台机器上测试的话,可以为Socket传入本地回送地址127.0.0.1。
使用方法和输出结果和实例1基本相同。
注意点有以下几个:
多线程的情况下,客户端Socket的关闭应当由处理这个连接的线程完成,因为服务器不知道应当什么时候关闭。
port指要绑定的端口;如果传入0,系统会自动选择一个可用的端口;
backlog指等待
4000
连接队列的最大长度;
bindAddr指一个特定的本地IP地址。默认情况下,如果一个主机有多个网络接口或IP地址,ServerSocket会在每个接口和IP地址上监听。如果设置了bindAddr,那么就只会监听这个地址上的入站连接。
无参构造器在创建对象时不会绑定端口,可以在之后使用bind()方法进行绑定。
ServerSocket简介
ServerSocket类包含了使用Java编写服务器所需的全部内容,其中包括创建新ServerSocket对象的构造函数、在指定端口监听连接的方法、配置各个服务器Socket选项的方法,以及一些常见的方法(如toString)。在Java中,一个服务器的生命周期如下:
使用ServerSocket的构造函数在一个特定端口创建一个新的ServerSocket;
ServerSocket使用其accept()方法监听这个端口的入站连接。accept()方法会一直阻塞,直到一个客户端尝试建立连接,此时方法会返回一个连接客户端和服务器的Socket对象;
根据服务器的类型调用Socket的getInputStream()和getOutputStream()获取输入/输出流;
服务器和客户端根据协议交互,直到关闭连接;
服务器或客户端关闭连接;
服务器回到步骤2,等待下一次连接。
下面看一个简单的服务器,用于获取时间:
实例1:Daytime服务器
public static void createDaytimeServer(){ try(ServerSocket server = new ServerSocket(13)){ while(true){ try(Socket connection = server.accept()){ Writer out = new OutputStreamWriter( connection.getOutputStream(), "UTF-8"); Date now = new Date(); out.write(now.toString() + "\r\n"); out.flush(); }catch (IOException e) { } } } catch (IOException e) { System.err.println("服务器意外退出"); } }
启动服务器:
createDaytimeServer();
请求服务器提供服务:
try(Socket socket = new Socket("127.0.0.1", 13)){ BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); System.out.println(in.readLine()); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } 输出: Wed Sep 13 09:34:09 CST 2017
注意点有以下几个:
客户端Socket与服务器Socket使用完毕后都需要调用close()关闭连接,Java 7及之后的版本可以利用try-with-resources实现。
启动服务器的代码和请求服务器提供服务的代码要分开执行,否则可能得不到输出。
服务器的代码中有两类异常,一类是某个客户端连接出错时抛出的,一类是服务器出错时抛出的,必须分别处理。
在同一台机器上测试的话,可以为Socket传入本地回送地址127.0.0.1。
多线程服务器
前面的服务器一次只能处理一个请求,如果有一个很慢的客户端先请求到了服务,会使得后面的客户端阻塞很长时间。一个比较好的方法是为每个连接提供一个线程。实例2:使用线程池的多线程Daytime服务器
public static void createPooledDaytimeServer(){ ExecutorService pool = Executors.newFixedThreadPool(50); try(ServerSocket server = new ServerSocket(13)){ while(true){ try { Socket connection = server.accept(); pool.execute(new DaytimeTask(connection)); } catch (IOException e) { } } } catch (IOException e) { System.err.println("服务器意外退出"); } } private static class DaytimeTask implements Runnable{ private Socket connection; DaytimeTask(Socket connection){ this.connection = connection; } @Override public void run(){ try(Writer out = new OutputStreamWriter( connection.getOutputStream(), "UTF-8")){ Date now = new Date(); out.write(now.toString() + "\r\n"); out.flush(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { connection.close(); } catch (IOException e) { e.printStackTrace(); } } } }
使用方法和输出结果和实例1基本相同。
注意点有以下几个:
多线程的情况下,客户端Socket的关闭应当由处理这个连接的线程完成,因为服务器不知道应当什么时候关闭。
日志(暂缺)
ServerSocket的其他构造器
public ServerSocket() throws IOException public ServerSocket(int port) throws IOException public ServerSocket(int port, int backlog) public ServerSocket(int port, int backlog, InetAddress bindAddr)
port指要绑定的端口;如果传入0,系统会自动选择一个可用的端口;
backlog指等待
4000
连接队列的最大长度;
bindAddr指一个特定的本地IP地址。默认情况下,如果一个主机有多个网络接口或IP地址,ServerSocket会在每个接口和IP地址上监听。如果设置了bindAddr,那么就只会监听这个地址上的入站连接。
无参构造器在创建对象时不会绑定端口,可以在之后使用bind()方法进行绑定。
性能优先级
利用setPerformancePreferences(int connectionTime,int latency,int bandwidth)可以设置服务器各项性能指标的优先级。例如:setPerformancePreferences(2,1,3)表示最大带宽是最重要的性能,最小延迟最不重要,连接时间居中。HTTP服务器
构建一个HTTP服务器的关键在于实现HTTP协议,即根据HTTP协议读取客户端的请求报文,并构造出对应的响应报文。下面是一个简单的单文件服务器,它无论接收到什么请求都返回200 OK,并传输一个指定文件。实例3:单文件HTTP服务器
public class SingleFileHTTPServer { private final byte[] content; private final byte[] header; private final int port = 80; private final String encoding = "UTF-8"; public SingleFileHTTPServer(String data,String mimeType) { this(data.getBytes(), mimeType); } public SingleFileHTTPServer(byte[] data, String mimeType){ this.content = data; //响应报文首部 String header = "HTTP/1.0 200 OK \r\n" + "Server: OneFile 2.0\r\n" + "Content-length:" + this.content.length + "\r\n" + "Content-type:" + mimeType + "; charset=" + encoding + "\r\n\r\n"; this.header = header.getBytes(Charset.forName("US-ASCII")); } public void start(){ ExecutorService pool = Executors.newFixedThreadPool(10); try(ServerSocket server = new ServerSocket(port)){ while(true){ try{ Socket connection = server.accept(); pool.execute(new HTTPHandler(connection)); }catch (IOException e) { e.printStackTrace(); }catch (RuntimeException e) { e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } } private class HTTPHandler implements Runnable{ private final Socket connection; HTTPHandler(Socket connection) { this.connection = connection; } @Override public void run() { try{ OutputStream out = new BufferedOutputStream(connection.getOutputStream()); InputStream in = new BufferedInputStream(connection.getInputStream()); StringBuilder request = new StringBuilder(80); while(true){ int c = in.read(); if(c == '\r' || c == '\n' || c == -1){ break; } request.append((char)c); } System.out.println(request.toString()); if(request.toString().indexOf("HTTP/") != -1){ out.write(header); } out.write(content); out.flush(); } catch (IOException e) { e.printStackTrace(); }finally{ try { connection.close(); } catch (IOException e) { e.printStackTrace(); } } } } //客户端测试用,会保存服务器发来的文件 public static void test(){ try { URL url = new URL("http://127.0.0.1"); URLConnection connection = url.openConnection(); InputStream in = new BufferedInputStream(connection.getInputStream()); int total = Integer.parseInt(connection.getHeaderField("Content-length")); File file = new File("C:\\Users\\qwer\\Desktop" + File.separator + "123.pdf"); try(FileOutputStream out = new FileOutputStream(file)){ byte[] bytes = new byte[64 * 1024]; int size; int len = 0; while(len < total){ size = in.read(bytes); out.write(bytes, 0, size); len += size; System.out.println(len); } out.flush(); System.out.println("finished"); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try { String filepath = "C:\\Users\\qwer\\Desktop\\osu\\漫画数据库.pdf"; Path path = Paths.get(filepath); byte[] data = Files.readAllBytes(path); //用于获取文件资源的MIME类型 String contentType = URLConnection.getFileNameMap().getContentTypeFor(filepath); SingleFileHTTPServer server = new SingleFileHTTPServer(data, contentType); server.start(); } catch (IOException e) { e.printStackTrace(); } // test(); } }
相关文章推荐
- Socket服务器整体架构概述
- 转:Socket服务器整体架构概述
- php简单socket服务器客户端代码实例
- c# Socket服务器整体架构概述
- Socket服务器整体架构概述
- Android客户端与PC服务器通过socket进行交互实例
- Socket服务器整体架构概述
- Socket服务器整体架构概述
- Android客户端与PC服务器通过socket进行交互实例
- Android客户端与PC服务器通过socket进行交互实例
- 一个简单的socket监听的服务器实例
- python服务器与android客户端socket通信实例
- SuperSocket 1.4系列文档(14) 多服务器实例和服务器实例之间的交互
- loadrunner使用socket协议来实现客户端对服务器产生压力实例。(通过发送心跳包,达到连接多个客户端的目的)
- Qt5 TcpSocket 客户端/服务器 通讯实例
- C++ Qt多线程 TcpSocket服务器实例
- Ubuntu下c语言实现并发服务器简单socket编程实例
- C#和PHP Socket通信实例代码(服务器与客户端通信)
- php简单socket服务器客户端代码实例
- Android客户端与PC服务器通过socket进行交互实例(转)