Web服务器开发小试牛刀——使用Socket通信
在web开发的过程中,大家都会接触到服务器这个词。
我最近在学Java Web的时候就用到了tomcat这个服务器,我就在想如何可以在自己编写一个服务器。
我们做开发的时候都知道,一个请求一般都会包括请求头、请求体,响应一般也会包括响应头和响应内容。
下面我们来写一下HttpServer服务器这个类:
package test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
public static void main(String[] args) {
int port;
ServerSocket serverSocket;
System.out.println("port = 8080(默认)");
port = 8080;
try {
serverSocket = new ServerSocket(port);
System.out.println("服务器正在监听端口:"+serverSocket.getLocalPort());
while(true){
final Socket socket = serverSocket.accept();
System.out.println("建立了一个与客户的一个新的TCP链接,客户地址为:"+socket.getInetAddress()+":"+socket.getPort());
service(socket);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void service(Socket socket) throws Exception {
InputStream socketInputStream = socket.getInputStream();
Thread.sleep(500);
int size = socketInputStream.available();
byte[] buffer = new byte[size];
socketInputStream.read(buffer);
String request = new String(buffer);
System.out.println(request); //打印HTTP请求数据
//解析HTTP请求
//获取HTTP请求的第一行
int endIndex = request.indexOf("\r\n");
if (endIndex == -1){
endIndex = request.length();
}
String firstLineOfRequest = request.substring(0,endIndex);
//解析HTTP请求第一行
String[] parts = firstLineOfRequest.split(" ");
String uri = "";
if (parts.length >= 2){
uri = parts[1];
}
String contentType;
if (uri.indexOf("html")!=-1 || uri.indexOf("htm")!=-1){
contentType = "text/html";
}
else if(uri.indexOf("jpg")!=-1 || uri.indexOf("jpeg")!= -1){
contentType = "image/jpeg";
}
else if (uri.indexOf("gif")!=-1){
contentType = "image/gif";
}
else {
contentType = "application/octet-stream"; //字节流类型
}
//创建HTTP响应结果
//HTTP响应第一行
String responseFirstLine = "HTTP/1.1 200 OK\r\n";
String responseHeader = "Content-Type:"+contentType+"\r\n\r\n";
InputStream inputStream = HttpServer.class.getResourceAsStream("root/" + uri);
//发送HTTP响应结果
OutputStream socketOutputStream = socket.getOutputStream();
socketOutputStream.write(responseFirstLine.getBytes());
socketOutputStream.write(responseHeader.getBytes());
int len = 0;
buffer = new byte[128];
while ((len = inputStream.read(buffer))!=-1){
socketOutputStream.write(buffer,0,len);
}
Thread.sleep(100);
socket.close();
}
}
整个服务器分为:
- 监听8080端口,建立TCP连接
- 处理请求头,截取请求的第一行的后缀名
- 由于提前设定好要访问的资源位于同级目录下的root文件夹下,所以采用HttpServer.class.getResourceAsStream("root/" + uri)获取输入流
- 设定200成功响应头,将响应的第一行、响应文件的类型以及响应主题发送回客户端
- 关闭连接
下面我们再写客户端的代码:
package cn.yuanzhen25800.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class HttpClient {
public static void main(String[] args) {
//确定请求的uri
String uri = "hello.html";
if (args.length != 0){
uri = args[0];
}
doGet("localhost",8080,uri);
}
public static void doGet(String host, int port, String uri) {
Socket socket = null;
try {
socket = new Socket(host,port);
} catch (IOException e) {
e.printStackTrace();
}
try {
StringBuffer stringBuffer = new StringBuffer("GET "+uri+" HTTP/1.1\r\n");
//HTTP请求头
stringBuffer.append("Accept: */*\r\n");
stringBuffer.append("Accept-Language: zh-cn\r\n");
stringBuffer.append("Accept-Encoding: gzip,deflate\r\n");
stringBuffer.append("User-Agent: HTTPClient\r\n");
stringBuffer.append("Host: localhost:8080\r\n");
stringBuffer.append("Connection: Keep-Alive\r\n");
//发送HTTP请求
OutputStream socketOutputStream = socket.getOutputStream();
socketOutputStream.write(stringBuffer.toString().getBytes());
Thread.sleep(2000);
//接收响应结果
InputStream socketInputStream = socket.getInputStream();
int size = socketInputStream.available();
byte[] buffer = new byte[size];
socketInputStream.read(buffer);
System.out.println(new String(buffer));
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端请求同服务器功能差不多,
分以下几个步骤:
- 建立一个TCP连接,确定服务器地址、端口以及请求的资源路径
- 编写请求头
- 发送请求头信息
- 接收响应结果并打印
下面是运行的结果图:
当然,服务器开启后在浏览器端也是可以请求的,下面是结果图:
以上是我的一些个人见解,如有错误欢迎大家斧正!