什么是socket
socket意为网络插槽
,当客户端和服务端之间交互的时候,会在客户端和服务端同时形成socket,通过socket来进行交互,发送信息通过socket,接受信息也是从socket中读。socket相当于一个文件,多数操作系统就是将socket实现成一个文件。
线程模型
当有一个请求过来时(相当于一个tcp连接),操作系统接到请求的时候,会先将请求放在一个叫pendingQueue
的队列中,如果这个队列满了,就会发生拒绝,实际项目中有时就会发现系统拒绝了一个连接。系统收到请求会一直放在这个队列里,直到Accept调用,它会从队列中拿出一个请求,在内核空间形成一个socket文件,然后对应内核中的socket会生成一个类似资源的句柄(FD),这个派发线程会无限的accept,拿请求形成socket生成FD。最终派发线程会将资源派发给工作线程,工作线程从socket中读用户发送的内容,然后处理,最后返回。返回不是原路返回的,而是工作线程直接将返回内容写入socket,内核中发现socket有写入,就会将写入信息返回用户。
socket简单的实现
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class socket1 {
public static void main(String[] args) {
try{
ServerSocket serverSocket = new ServerSocket(8080);
for(;;){
//阻塞操作,内核中pendingQueue中没有请求时会阻塞,等待有请求后拿到请求继续
Socket socket = serverSocket.accept();
System.out.println("收到请求");
//从socket中读信息
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder requestBuilder = new StringBuilder();
String line = "";
while(!(line = bufferedReader.readLine()).isEmpty()){
requestBuilder.append(line+'\n');
}
System.out.println(requestBuilder.toString());
//返回信息
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//按照http协议返回信息(后边继续学习http协议)
bufferedWriter.write("HTTP/1.1 200 OK\n\nhello world\n");
bufferedWriter.flush();
socket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
浏览器访问:
控制台输出的请求信息:
socket多线程模型及request/response简单封装
1.Request简单封装,主要用于读取请求内容
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpParser;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Request {
static Pattern methodRegex = Pattern.compile("(GET|PUT|POST|DELETE|OPTIONS|TRACE|HEAD)");
private final String body;
private final String method;
private final HashMap<String, String> headers;
public Request(Socket socket) throws IOException {
DataInputStream iptStream = new DataInputStream(socket.getInputStream());
//读取请求方法
// GET /path HTTP/1.1
String methodLine = HttpParser.readLine(iptStream, "UTF-8");
Matcher matcher = methodRegex.matcher(methodLine);
matcher.find();
String method = matcher.group();
//读取请求头
// Content-Type:xxxx
// Length : xxx
Header[] headers = HttpParser.parseHeaders(iptStream, "UTF-8");
HashMap headMap = new HashMap<String, String>();
for(Header h : headers) {
headMap.put(h.getName(), h.getValue());
}
//读取请求体body
BufferedReader bufferReader = new BufferedReader(new InputStreamReader(iptStream));
StringBuilder body = new StringBuilder();
char[] buffer = new char[1024];
while(iptStream.available() > 0) {
bufferReader.read(buffer);
body.append(buffer);
}
this.body = body.toString();
this.method = method;
this.headers = headMap;
}
public String getBody() {
return body;
}
public HashMap<String, String> getHeaders() {
return headers;
}
}
2.Response简单封装,主要用于返回数据
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.HashMap;
public class Response {
Socket socket;
private int status;
static HashMap<Integer, String> codeMap;
public Response(Socket socket) {
this.socket = socket;
if(codeMap == null) {
codeMap = new HashMap<>();
codeMap.put(200, "OK");
}
}
public void send(String msg) throws IOException {
String resp = "HTTP/1.1 " + this.status + " " + this.codeMap.get(this.status) + "\n";
resp += "\n";
resp += msg;
this.sendRaw(resp);
}
public void sendRaw(String msg) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(msg);
bufferedWriter.flush();
socket.close();
}
}
3.socket服务类
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
ServerSocket serverSocket;
IHandlerInterface httpHandler;
public Server(IHandlerInterface httpHandler){
this.httpHandler = httpHandler;
}
public void listen(int port) throws IOException {
serverSocket = new ServerSocket(port);
while(true) {
this.accept();
}
}
void accept() throws IOException {
Socket socket = serverSocket.accept();
//接收到请求后,改为多线程方式来处理请求
new Thread(() -> {
try {
this.handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void handler(Socket socket) throws IOException {
Request request = new Request(socket);
Response response = new Response(socket);
this.httpHandler.handler(request, response);
}
}
4.main启动类
import java.io.IOException;
public class TestMain {
public static void main(String[] args) throws IOException {
Server server = new Server((req, resp) -> resp.send("<html><body><h1>Hello world!</h1></body></html>"));
server.listen(8080);
}
}
简单认识NIO
NIO(JDK1.4)模型是一种同步非阻塞IO,主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(多路复用器)。通过Channel注册到Selector上的状态来实现一种客户端与服务端的通信。
传统IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
ServerSocketChannel ssc;
public void listen(int port) throws IOException {
ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(port));
// true阻塞(Reactive) false非阻塞 (Reactor)
ssc.configureBlocking(false);
Selector selector = Selector.open();
ssc.register(selector, ssc.validOps(), null);
ByteBuffer buffer = ByteBuffer.allocate(1024*16);
for(;;) {
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
if(key.isAcceptable()) {
SocketChannel channel = ssc.accept();
if(channel == null) {
continue;
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else {
SocketChannel channel = (SocketChannel)key.channel();
buffer.clear();
channel.read(buffer);
String request = new String(buffer.array());
System.out.println(request);
buffer.clear();
buffer.put("HTTP/1.1 200 ok\n\nHello NIO!!".getBytes());
buffer.flip();
channel.write(buffer);
channel.close();
}
}
}
}
public static void main(String[] argv) throws IOException {
NioServer server = new NioServer();
server.listen(8001);
}
}