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

Java网络编程 Ch9 服务器Socket

2018-03-16 09:19 435 查看
基本流程

////在指定端口新建ServerSocket
try (ServerSocket server = new ServerSocket(1024)){
while (true) {
//accept阻塞调用,一直等待
try (Socket connection = server.accept()) {
Writer out = new OutputStreamWriter(connection.getOutputStream());
Date now = new Date();
//回车换行对结束。不能用System.getProperty("line.separator")或者sout
out.write(now.toString()+"\r\n");
//刷新输出
out.flush();
//try-with-resource会自动关闭connection,否则需要在finally'中关闭
connection.close();
}catch (IOException ex){
//仅关闭该连接,不影响服务器
}
}catch (IOException ex){
//关闭服务器
System.err.println();
}
}


多线程服务器

package Ch8;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PooledDaytimeServer {
public final static int PORT = 13;

public static void main(String[] args) {
//创建size为50的线程池,避免大量创建线程导致资源耗尽。
ExecutorService pool = Executors.newFixedThreadPool(50);

try(ServerSocket server = new ServerSocket(PORT)){
while (true){
//这里不适用try-with-resource是因为,到达while末尾时,会自动关闭Socket,而此时该线程可能还没使用完Socket。
try{
Socket connection = server.accept();
//将事件包装到新线程中。
Callable<Void> task = new DayTimeTask(connection);
//将Callable提交给executor。任务队列。与Thread子类启动线程不同。
pool.submit(task);
}catch (IOException ex){}
}
}catch (IOException ex){
System.err.println("Couldn't start server");
}
}

private static class DayTimeTask implements Callable<Void>{
private Socket connection;
DayTimeTask(Socket connection){
this.connection = connection;
}
@Override
public Void call(){
try{
Writer out = new OutputStreamWriter(connection.getOutputStream());
Date now = new Date();
out.write(now.toString()+"\r\n");
out.flush();
}catch (IOException ex){
System.err.println(ex);
}finally {
try{
connection.close();
}catch (IOException ex){}
}
return null;
}
}
}


检查ServerSocket是否打开

public static boolean isOpen(ServerSocket ss){
return ss.isBound()&&!ss.isClosed();
}


日志???

构造服务器Socket

java

public ServerSocket() throws BindException, IOException

public ServerSocket(int port) throws BindException, IOException

public ServerSocket(int port,int queueLength) throws BindException, IOException

//端口号,入站连接最大数,特定本地IP地址

public ServerSocket(int port,int queueLength, InetAddress bindAddress) throws BindException, IOException

//无法创建并绑定到指定端口,抛出BindException。

//端口已经被使用IOException


构造但不绑定端口

无参构造器,使用

public void bind(SocketAddress endpoint)
public void bind(SocketAddress endpoint, int queueLength) throws IOException


获取属性

Socket选项

在accept()之前设置

SO_TIMEOUT 0永不超时

public void setSoTimeout(int timeout)
getSoTimeout()


SO_REUSEADDR

SO_RCVBUF

服务类型

public void setPerformancePreference(int connectionTime, int latency, int bandwith )
//(2,1,3)表示延迟最不重要,带宽最重要


HTTP服务器???

单文件服务器

/***********
4000
*******************************服务器*****************************************/
package Ch8;

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.concurrent.*;
import java.util.logging.*;

public class SingleFileHTTPServer {
private static final Logger logger = Logger.getLogger("SingleFileHTTPServer");

private final byte[] content;
private final byte[] header;
private final int port;
private final String encoding;
//构造函数
public SingleFileHTTPServer(String data, String encoding, String mimeType, int port) throws UnsupportedEncodingException{
this(data.getBytes(encoding),encoding,mimeType,port);
}

public SingleFileHTTPServer(byte[] data, String encoding, String mimeType, int port){
this.content = data;
this.port = port;
this.encoding = encoding;
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(100);
try(ServerSocket server = new ServerSocket(this.port)){
logger.info("Accepting connections on port "+server.getLocalPort());
logger.info("Data to be sent:");
logger.info(new String(this.content, encoding));

while(true){
try{
Socket connection = server.accept();
pool.submit(new HTTPHandler(connection));
}catch (IOException ex){
logger.log(Level.WARNING, "Exception accepting connection",ex);
}catch (RuntimeException ex){
logger.log(Level.SEVERE, "Unexpected error",ex);}
}
}catch (IOException ex){
logger.log(Level.SEVERE,"Could not start server",ex);
}
}

private class HTTPHandler implements Callable<Void> {
private final Socket connection;
HTTPHandler(Socket connection){
this.connection = connection;
}

@Override
public Void call() throws IOException{
try {
OutputStream out = new BufferedOutputStream(connection.getOutputStream());
InputStream in = new BufferedInputStream(connection.getInputStream());

StringBuilder request = new StringBuilder(80);
while (true) {
//如果没有输入,in会一直等待,阻塞。Connection没有关闭,Client有可能再发送消息。或者由于网络延迟,丢包。流暂时不能结束。
int c = in.read();
if (c == '\r' || c == '\n' || c == -1) break;
request.append((char) c);
}

if (request.toString().indexOf("HTTP/") != -1) {
System.out.println("YES");
out.write(header);
}
//因为finally中将connection关闭了,所以client的reader能读到末尾。
out.write(content);
/*Writer out1 = new OutputStreamWriter(out);
out1.write("\r\n");*/
out.flush();

}catch (IOException ex){
logger.log(Level.WARNING,"Error writing to client",ex);
}finally {
connection.close();
}
return null;
}
}
//完成服务器资源配置,属性设置,以及启动服务器。
public static void main(String[] args) {
int port;
try{
port = Integer.parseInt(args[1]);
if(port<1||port>65535) port = 80;
}catch (RuntimeException ex){
port = 80;
}

String encoding = "UTF-8";
if(args.length>2) encoding = args[2];
try{
Path path = Paths.get(args[0]);
byte[] data = Files.readAllBytes(path);
String contentType = URLConnection.getFileNameMap().getContentTypeFor(args[0]);
SingleFileHTTPServer server = new SingleFileHTTPServer(data, encoding, contentType, port);
server.start();
}catch (ArrayIndexOutOfBoundsException ex){
System.out.println("Usage: java SingleFileHTTPServer filename port encoding");
}catch (IOException ex){
logger.severe(ex.getMessage());
}
}

}

/*************************************客户端************************************************/
package Ch8;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class TryGet {
public static void main(String[] args) {
Socket socket = new Socket();
try {
SocketAddress address = new InetSocketAddress("localhost", 80);
socket.connect(address,10);
InputStream in = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(in,"UTF-8");

OutputStream out = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(out);

//要加\r\n的原因是,HTTP规范决定了用\r\n换行。
/*也有效
OutputStream out = new BufferedOutputStream(socket.getOutputStream());
out.write("HTTP1.1\r\n".getBytes("UTF-8"));
out.flush();*/
//不加\r\n,不会有结果。
writer.write("HTTP/1.1\r\n");
//不刷新缓存内容也不会有结果,
writer.flush();

String result = "";
int tem;
while((tem=reader.read())!=-1){
result+=(char)tem;
}
System.out.println(result);

}catch (IOException ex){
System.out.println("err");
}
}
}


功能完备的HTTP服务器

/***********************************服务器启动***********************************************/
package Ch8;

import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.logging.*;

public class JHTTP {
//创建JHTTP的日志记录器
private static final Logger logger = Logger.getLogger(JHTTP.class.getCanonicalName());

private static final int NUM_THREADS = 50;
private static final String INDEX_FILE = "index.html";

private final File rootDirectory;
private final int port;

public JHTTP(File rootDirectory, int port) throws IOException{
//判断是否是目录
if(!rootDirectory.isDirectory()){
throw new IOException(rootDirectory+" does not exist as a directory");
}
this.rootDirectory = rootDirectory;
this.port = port;
}

public void start() throws IOException{
ExecutorService pool = Executors.newFixedThreadPool(NUM_THREADS);
try(ServerSocket server = new ServerSocket(port)){
//便于记录多个线程时,每个线程进入的端口。
logger.info("Accepting connections on port "+server.getLocalPort());
logger.info("Document Root: "+rootDirectory);

while(true){
try{
Socket request = server.accept();
Runnable r = new RequestProcessor(rootDirectory,INDEX_FILE,request);
pool.submit(r);
}catch (IOException ex){
logger.log(Level.WARNING,"Error" +
" accepting connection",ex);
}
}
}

}

public static void main(String[] args) {
File docroot;
try{
docroot = new File(args[0]);
}catch (ArrayIndexOutOfBoundsException ex){
System.out.println("Usage: java JHTTP docroot port");
return;
}

int port;
try{
port = Integer.parseInt(args[1]);
if(port<0||port>65535) port = 80;
}catch (RuntimeException ex){
port =80;
}

try{
JHTTP webserver = new JHTTP(docroot,port);
webserver.start();
}catch (IOException ex){
logger.log(Level.SEVERE,"Server cound not start",ex);
}
}

}

/***********************************服务器业务逻辑****************************************/
package Ch8;

import java.net.*;
import java.nio.file.Files;
import java.util.*;
import java.io.*;
import java.util.logging.*;

public class RequestProcessor implements Runnable {
private final static Logger logger=Logger.getLogger(RequestProcessor.class.getCanonicalName());

private File rootDirectory;
private String indexFileName = "index.html";
private Socket connection;
//构造函数,将Server的参数传入。
public RequestProcessor(File rootDirectory, String indexFileName, Socket connection){
if(rootDirectory.isFile()) {throw  new IllegalArgumentException("rootDirectory must be a dirctory, not a file");
}

try{
rootDirectory = rootDirectory.getCanonicalFile();
}catch (IOException ex){
}

this.rootDirectory = rootDirectory;

if(indexFileName!=null) this.indexFileName = indexFileName;
this.connection = connection;
}

@Override
public void run(){
String root = rootDirectory.getPath();
try{
OutputStream raw = new BufferedOutputStream(connection.getOutputStream());
Writer out = new OutputStreamWriter(raw);
//字节转为字符时,编码和解码字符集一致,才能正确。
Reader in = new InputStreamReader(new BufferedInputStream(connection.getInputStream()),"UTF-8");
StringBuilder requestLine = new StringBuilder();
//读取第一行
while(true){
int c = in.read();
if(c=='\r'||c=='\n') break;
requestLine.append((char)c);
}

String get = requestLine.toString();
// System.out.println(get);
logger.info(connection.getRemoteSocketAddress()+" "+get);
//处理首行。 方法 URI 版本。
//使用空格来划分,所以文件名不能含空格,且注意文件名的编码格式保持一致。
String[] tokens = get.split("\\s+"); String method = tokens[0]; String version = "";
if(method.equals("GET")){
String fileName = tokens[1];
if(fileName.endsWith("/")) fileName+=indexFileName;
//获取文件内容格式
String contentType = URLConnection.getFileNameMap().getContentTypeFor(fileName);
if(tokens.length>2) version = tokens[2];
//去掉/
File theFile = new File(rootDirectory, fileName.substring(1,fileName.length()));

if(theFile.canRead()&&theFile.getCanonicalPath().startsWith(root)){
//为什么要转为Path,path和file区别
byte[] theData = Files.readAllBytes(theFile.toPath());
//发送headers
if(version.startsWith("HTTP/")){
//参数提供内容,seadHeader提供格式。
sendHeader(out,"HTTP/1.0 200 ok",contentType,theData.length);
}
//theData是字节数组
raw.write(theData);
raw.flush();
}else {
String body = new StringBuilder("<HTML>\r\n").
append("<HEAD><TITLE>File not Found</TITLE>\r\n").
append("</HEAD>\r\n").
append("<BODY>")
.append("<H1>HTTP Error 404: File Not Found</H1>\r\n")
.append("</BODY></HTML>\r\n").toString();
if(version.startsWith("HTTP/")){
sendHeader(out,"HTTP/1.0 404 File Not Found","text/html;charset=uft-8",body.length());
}
out.write(body);
out.flush();
}
}else{
String body = new StringBuilder("<HTML>\r\n").
append("<HEAD><TITLE>Not Implemented</TITLE>\r\n").
append("</HEAD>\r\n").
append("<BODY>")
.append("<H1>HTTP Error 501: Not Implemented</H1>\r\n")
.append("</BODY></HTML>\r\n").toString();
if(version.startsWith("HTTP/")){
sendHeader(out,"HTTP/1.0 501 Not Implemented","text/html;charset=uft-8",body.length());
}
out.write(body);
out.flush();
}

}catch(IOException ex){
logger.log(Level.WARNING,"Error talking to "+connection.getRemoteSocketAddress(),ex);
}finally {
try{
connection.close();
}catch (IOException ex){}
}
}

private void sendHeader(Writer out, String responseCode,String contentType, int length) throws IOException{
out.write(responseCode+"\r\n");
Date now = new Date();
out.write("Date: "+now+"\r\n");
out.write("Server: JHTTP 2.0\r\n");
out.write("Content-length: "+length+"\r\n");
out.write("Content-type: "+contentType+"\r\n\r\n");
out.flush();
}
}

/***********************************客户端请求*****************************************/
package Ch8;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class TryGet {
public static void main(String[] args) {
Socket socket = new Socket();
try {
SocketAddress address = new InetSocketAddress("localhost", 80);
socket.connect(address,1000);
InputStream in = socket.getInputStream();
//在读取输入流时,设置字符集,才可以正确解码。
InputStreamReader reader = new InputStreamReader(new BufferedInputStream(in),"UTF-8");

OutputStream out = new BufferedOutputStream(socket.getOutputStream());
out.write("GET /Ch3线程.md HTTP/1.1 \r\n".getBytes("UTF-8"));
out.flush();

String result = "";
int tem;
while((tem=reader.read())!=-1){
result+=(char)tem;
}
System.out.println(result);

}catch (IOException ex){
System.out.println("err");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java网络编程