Java 网络编程 —— 创建非阻塞的 HTTP 服务器
HTTP 概述
HTTP 客户程序必须先发出一个 HTTP 请求,然后才能接收到来自 HTTP 服器的响应,浏览器就是最常见的 HTTP 客户程序。HTTP 客户程序和 HTTP 服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写。HTTP 严格规定了 HTTP 请求和 HTTP 响应的数据格式,只要 HTTP 服务器与客户程序都遵守 HTTP,就能彼此看得懂对方发送的消息
1. HTTP 请求格式
下面是一个 HTTP 请求的例子
POST /hello.jsp HTTP/1.1
Accept:image/gif, image/jpeg, */*
Referer: http://localhost/login.htm
Accept-Language: en,zh-cn;q=0.5
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 10.0)
Host: localhost
Content-Length:43
Connection: Keep-Alive
Cache-Control: no-cache
username=root&password=12346&submit=submit
HTTP 规定,HTTP 请求由三部分构成,分别是:
请求方法、URI、HTTP 的版本
- HTTP 请求的第一行包括请求方式、URI 和协议版本这三项内容,以空格分开:
POST /hello.jsp HTTP/1.1
- HTTP 请求的第一行包括请求方式、URI 和协议版本这三项内容,以空格分开:
请求头(Request Header)
请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器的类型、所用的语言、请求正文的类型,以及请求正文的长度等
Accept:image/gif, image/jpeg, */*
Referer: http://localhost/login.htm
Accept-Language: en,zh-cn;q=0.5 //浏览器所用的语言
Content-Type: application/x-www-form-urlencoded //正文类型
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 10.0) //浏览器类型
Host: localhost //远程主机
Content-Length:43 //正文长度
Connection: Keep-Alive
Cache-Control: no-cache
请求正文(Request Content)
HTTP 规定,请求头和请求正文之间必须以空行分割(即只有 CRLF 符号的行),这个空行非常重要,它表示请求头已经结束,接下来是请求正文,请求正文中可以包含客户以 POST 方式提交的表单数据
username=root&password=12346&submit=submit
2. HTTP 响应格式
下面是一个 HTTP 响应的例子
HTTP/1.1 200 0K
Server: nio/1.1
Content-type: text/html; charset=GBK
Content-length:97
<html>
<head>
<title>helloapp</title>
</head>
<body >
<h1>hello</h1>
</body>
</htm1>
HTTP 响应也由三部分构成,分别是:
HTTP 的版本、状态代码、描述
- HTTP 响应的第一行包括服务器使用的 HTTP 的版本、状态代码,以及对状态代码的描述,这三项内容之间以空格分割
响应头 (Response Header)
响应头也和请求头一样包含许多有用的信息,例如服务器类型、正文类型和正文长度等
Server: nio/1.1 //服务器类型
Content-type: text/html; charset=GBK //正文类型
Content-length:97 //正文长度
响应正文(Response Content)
响应正文就是服务器返回的具体的文档,最常见的是 HTML 网页。HTTP 响应头与响应正文之间也必须用空行分隔
<html>
<head>
<title>helloapp</title>
</head>
<body >
<h1>hello</h1>
</body>
</htm1>
创建阻塞的 HTTP 服务器
下例(SimpleHttpServer)创建了一个非常简单的 HTTP 服务器,它接收客户程序的 HTTP 请求,把它打印到控制台。然后对 HTTP 请求做简单的解析,如果客户程序请求访问 login.htm,就返回该网页,否则一律返回 hello.htm 网页。login.htm 和 hello.htm 文件位于 root 目录下
SimpleHttpServer 监听 80 端口,按照阻塞模式工作,采用线程池来处理每个客户请求
public class SimpleHttpServer {
private int port = 80;
private ServerSocketChannel serverSocketChannel = null;
private ExecutorService executorService;
private static final int POOL MULTIPLE = 4;
private Charset charset = Charset.forName("GBK");
public SimpleHttpServer() throws IOException {
executorService= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL MULTIPLE);
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("服务器启动");
}
public void service() {
while (true) {
SocketChannel socketChannel = null;
try {
socketChannel = serverSocketChannel.accept();
executorService.execute(new Handler(socketChannel));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String args[])throws IOException {
new SimpleHttpServer().service();
}
public String decode(ByteBuffer buffer) {......} //解码
public ByteBuffer encode(String str) {......} //编码
//Handler是内部类,负责处理HTTP请求
class Handler implements Runnable {
private SocketChannel socketChannel;
public Handler(SocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
public void run() {
handle(socketChannel);
}
public void handle(SocketChannel socketChannel) {
try {
Socket socket = socketChannel.socket();
ByteBuffer buffer = ByteBuffer.allocate(1024);
//接收HTTP请求,假定其长度不超过1024字节
socketChannel.read(buffer);
buffer.flip();
String request = decode(buffer);
//打印HTTP请求
System.out.print(request);
//生成HTTP响应结果
StringBuffer sb = new StringBuffer("HTTP/1.1 200 0K\r\n");
sb.append("Content-Type:text/html\r\n\r\n");
//发送HTTP响应的第1行和响应头
socketChannel.write(encode(sb.toString()));
FileInputStream in;
//获得HTTP请求的第1行
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
if(firstLineOfRequest.indexOf("login.htm") != -1) {
in = new FileInputStream("login.htm");
} else {
in = new FileInputStream("hello.htm");
}
FileChannel fileChannel = in.getChannel();
//发送响应正文
fileChannel.transferTo(0, fileChannel.size(), socketChannel);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(socketChannel != null) {
//关闭连接
socketChannel.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
创建非阻塞的 HTTP 服务器
下面是本节所介绍的非阻塞的 HTTP 服务器范例的模型
- HttpServer:服务器主程序,由它启动服务器
- AcceptHandler:负责接收客户连接
- RequestHandler:负责接收客户的 HTTP 请求,对其解析,然后生成相应的 HTTP 响应,再把它发送给客户
- Request:表示 HTTP 请求
- Response:表示 HTTP 响应
- Content:表示 HTTP 响应的正文
1. 服务器主程序 HttpServer
HttpServer 仅启用了单个主线程,采用非阻塞模式来接收客户连接,以及收发数据
public class HttpServer {
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private int port = 80;
private Charset charset = Charset.forName("GBK");
public HttpServer() throws IOException {
//创建Selector和ServerSocketChannel
//把ServerSocketchannel设置为非阻塞模式,绑定到80端口
......
}
public void service() throws IOException {
//注册接收连接就绪事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, new AcceptHandler());
while(true) {
int n = selector.select();
if(n==0) continue;
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while(it.hasNext()) {
SelectionKey key = null;
try {
key = (SelectionKey) it.next();
it.remove();
final Handler handler = (Handler) key.attachment();
handler.handle(key); //由 Handler 处理相关事件
} catch(IOException e) {
e.printStackTrace();
try {
if(key != null) {
key.cancel();
key.channel().close();
}
} catch(Exception ex) {
e.printStackTrace();
}
}
}
}
}
public static void main(String args[])throws Exception {
final HttpServer server = new HttpServer();
server.service();
}
}
2. 具有自动增长的缓冲区的 ChannelIO 类
自定义的 ChannelIO 类对 SocketChannel 进行了包装,增加了自动增长缓冲区容量的功能。当调用 socketChannel.read(ByteBuffer bufer) 方法时,如果 buffer 已满,即使通道中还有未接收的数据,read 方法也不会读取任何数据,而是直接返回 0,表示读到了零字节
为了能读取通道中的所有数据,必须保证缓冲区的容量足够大。在 ChannelIO 类中有一个 requestBuffer 变量,它用来存放客户的 HTTP 请求数据,当 requestBuffer 剩余容量已经不足 5%,并且还有 HTTP 请求数据未接收时,ChannellO 会自动扩充 requestBuffer 的容量,该功能由 resizeRequestBuffer() 方法完成
public class ChannelIO {
protected SocketChannel socketChannel;
protected ByteBuffer requestBuffer; //存放请求数据
private static int requestBufferSize = 4096;
public ChannelIO(SocketChannel socketChannel, boolean blocking) throws IOException {
this.socketChannel = socketChannel;
socketChannel.configureBlocking(blocking); //设置模式
requestBuffer = ByteBuffer.allocate(requestBufferSize);
}
public SocketChannel
() {
return socketChannel;
}
/**
* 如果原缓冲区的剩余容量不够,就创建一个新的缓冲区,容量为原来的两倍
* 并把原来缓冲区的数据拷贝到新缓冲区
*/
protected void resizeRequestBuffer(int remaining) {
if (requestBuffer.remaining() < remaining) {
ByteBuffer bb = ByteBuffer.allocate(requestBuffer.capacity() * 2);
requestBuffer.flip();
bb.put(requestBuffer); //把原来缓冲区中的数据拷贝到新的缓冲区
requestBuffer = bb;
}
}
/**
* 接收数据,把它们存放到requestBuffer
* 如果requestBuffer的剩余容量不足5%
* 就通过resizeRequestBuffer()方法扩充容量
*/
public int read() throws IOException {
resizeRequestBuffer(requestBufferSize/20);
return socketChannel.read(requestBuffer);
}
/** 返回requestBuffer,它存放了请求数据 */
public ByteBuffer getReadBuf() {
return requestBuffer;
}
/** 发送参数指定的 ByteBuffer 的数据 */
public int write(ByteBuffer src) throws IOException {
return socketChannel.write(src);
}
/** 把FileChannel的数据写到SocketChannel */
public long transferTo(FileChannel fc, long pos, long len) throws IOException {
return fc.transferTo(pos, len, socketChannel);
}
/** 关闭SocketChannel */
public void close() throws IOException {
socketChannel.close();
}
}
3. 负责处理各种事件的 Handler 接口
Handler 接口负责处理各种事件,它的定义如下:
public interface Handler {
public void handle(SelectionKey key) throws IOException;
}
Handler 接口有 AcceptHandler 和 RequestHandler 两个实现类。AcceptHandler 负责处理接收连接就绪事件,RequestHandler 负责处理读就绪和写就绪事件。更确切地说,RequestHandler 负责接收客户的 HTTP 请求,以及发送 HTTP 响应
4. 负责处理接收连接就绪事件的 AcceptHandler类
AcceptHandler 负责处理接收连接就绪事件,获得与客户连接的 SocketChannel,然后向 Selector 注册读就绪事件,并且创建了一个 RequestHandler,把它作为 SelectionKey 的附件。当读就绪事件发生时,将由这个 RequestHandler 来处理该事件
public class AcceptHandler implements Handler {
public void handle(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
//在非阻塞模式下,serverSocketChannel.accept()有可能返回null
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) return;
//ChannelIO设置为采用非阻塞模式
ChannelIO cio = new ChannelIO(socketChannel, false);
RequestHandler rh = new RequestHandler(cio);
//注册读就绪事件,把RequestHandler作为附件
socketChannel.register(key.selector(), SelectionKey.OP_READ, rh);
}
}
5. 负责接收 HTTP 请求和发送 HTTP 响应的 RequestHandler 类
RequestHandler 先通过 ChannelIO 来接收 HTTP 请求,当接收到 HTTP 请求的所有数据后,就对 HTTP 请求数据进行解析,创建相应的 Request 对象,然后依据客户的请求内容,创建相应的 Response 对象,最后发送 Response 对象中包含的 HTTP 响应数据。为了简化程序,RequestHandler 仅仅支持 GET 和 HEAD 两种请求方式
public class RequestHandler implements Handler {
private ChannelIO channelIO;
//存放HTTP请求的缓冲区
private ByteBuffer requestByteBuffer = null;
//表示是否已经接收到HTTP请求的所有数据
private boolean requestReceived = false;
//表示HTTP请求
private Request request = null;
//表示HTTP响应
private Response response = null;
RequestHandler(ChannelIO channelIO) {
this.channelIO = channelIO;
}
/** 接收HTTP请求,发送HTTP响应 */
public void handle(SelectionKey sk) throws IOException {
try {
//如果还没有接收HTTP请求的所有数据,就接收HTTP请求
if (request == null) {
if (!receive(sk)) return;
requestByteBuffer.flip();
//如果成功解析了HTTP请求,就创建一个Response对象
if (parse()) build();
try {
//准备HTTP响应的内容
response.prepare();
} catch (IOException x) {
response.release();
response = new Response(Response.Code.NOT_FOUND, new StringContent(x.getMessage()));
response.prepare();
}
if (send()) {
//如果HTTP响应没有发送完毕,则需要注册写就绪事件,以便在写就绪事件发生时继续发送数据
sk.interestOps(SelectionKey.OP_WRITE);
} else {
//如HTTP响应发送完毕,就断开底层连接,并且释放Response占用资源
channelIO.close();
response.release();
}
} else {
//如果已经接收到HTTP请求的所有数据
//如果HTTP响应发送完毕
if (!send()) {
channelIO.close();
response.release();
}
}
} catch (IOException e) {
e.printStackTrace();
channelIO.close();
if (response != null) {
response.release();
}
}
}
/**
* 接收HTTP请求,如果已经接收到了HTTP请求的所有数据,就返回true,否则返回false
*/
private boolean receive(SelectionKey sk) throws IOException {
ByteBuffer tmp = null;
//如果已经接收到HTTP请求的所有数据,就返回true
if (requestReceived) return true;
//如果已经读到通道的末尾,或者已经读到HTTP请求数据的末尾标志,就返回true
if ((channelIO.read() < 0) || Request.isComplete(channelIO.getReadBuf())) {
requestByteBuffer = channelIO.getReadBuf();
return (requestReceived = true);
}
return false;
}
/**
* 通过Request类的parse()方法,解析requestByteBuffer的HTTP请求数据
* 构造相应的Request对象
*/
private boolean parse() throws IOException {
try {
request = Request.parse(requestByteBuffer);
return true;
} catch (MalformedRequestException x) {
//如果HTTP请求的格式不正确,就发送错误信息
response = new Response(Response.Code.BAD_REQUEST, new StringContent(x))
}
return false;
}
/** 创建HTTP响应 */
private void build() throws IOException {
Request.Action action = request.action();
//仅仅支持GET和HEAD请求方式
if ((action != Request.Action.GET) && (action != Request.Action.HEAD)) {
response = new Response(Response.Code.METHOD_NOT_ALLOWED, new StringContent("Method Not Allowed"));
} else {
response = new Response(Response.Code.OK, new FileContent(request.uri()), action);
}
}
/** 发送HTTP响应,如果全部发送完毕,就返回false,否则返回true */
private boolean send() throws IOException {
return response.send(channelIO);
}
}
6. 代表 HTTP 请求的 Request 类
RequestHandler 通过 ChannelIO 读取 HTTP 请求数据时,这些数据被放在 requestByteBuffer 中。当 HTTP 请求的所有数据接收完毕,就要对 requestByteBufer 的数据进行解析,然后创建相应的 Request 对象。Request 对象就表示特定的 HTTP 请求
public class Request {
//枚举类,表示HTTP请求方式
static enum Action {
GET,PUT,POST,HEAD;
}
public static Action parse(String s) {
if (s.equals("GET"))
return GET;
if (s.equals("PUT"))
return PUT;
if (s.equals("POST"))
return POST;
if (s,equals("HEAD"))
return HEAD;
throw new IllegalArgumentException(s);
}
private Action action; //请求方式
private String version; //HTTP版本
private URI uri; //URI
public Action action() { return action; }
public String version() { return version; }
public URI uri() { return uri; }
private Request(Action a, String V, URI u) {
action = a;
version = v;
uri =u;
}
public String toString() {
return (action + " " + version + " " + uri);
}
private static Charset requestCharset = Charset.forName("GBK");
/**
* 判断ByteBuffer是否包含HTTP请求的所有数据
* HTTP请求以”r\n\r\n”结尾
*/
public static boolean isComplete(ByteBuffer bb) {
ByteBuffer temp = bb.asReadOnlyBuffer();
temp.flip();
String data = requestCharset.decode(temp).toString();
if(data.indexOf("r\n\r\n") != -1) {
return true;
}
return false;
}
/**
* 删除请求正文
*/
private static ByteBuffer deleteContent (ByteBuffer bb) {
ByteBuffer temp = bb.asReadOnlyBuffer();
String data = requestCharset.decode(temp).toString();
if(data.indexOf("\r\n\r\n") != -1) {
data = data.substrinq(0, data.indexOf("\r\n\r\n") + 4);
return requestCharset.encode(data);
}
return bb;
}
/**
* 设定用于解析HTTP请求的字符串匹配模式,对于以下形式的HTTP请求
* GET /dir/file HTTP/1.1
* Host: hostname
* 将被解析成:
* group[l] = "GET”
* group[2]="/dir/file"
* group[3]="1.1"
* group[4]="hostname"
*/
private static Pattern requestPattern =
Pattern.compile("\\A([A-Z]+) +([^]+) +HTTP/([0-9\\.]+)$"
+ ",*^Host:([]+)$.*\r\n\r\n\\z",
Pattern.MULTILINE | Pattern.DOTALL);
/** 解析HTTP请求,创建相应的Request对象 */
public static Request parse(ByteBuffer bb) throws MalformedRequestException {
bb = deleteContent(bb); //删除请求正文
CharBuffer cb = requestCharset.decode(bb); //解码
Matcher m = requestPattern.matcher(cb); //进行字符串匹配
//如果HTTP请求与指定的字符串式不匹配,说明请求数据不正确
if (!m.matches())
throw new MalformedRequestException();
Action a;
//获得请求方式
try {
a = Action.parse(m.group(1));
} catch (IllegalArgumentException x) {
throw new MalformedRequestException();
}
//获得URI
URI u;
try {
u=new URI("http://" + m.group(4) + m.group(2));
} catch (URISyntaxException x) {
throw new MalformedRequestException();
}
//创建一个Request对象,并将其返回
return new Request(a, m.group(3), u);
}
}
7. 代表 HTTP 响应的 Response 类
Response 类表示 HTTP 响应,它有三个成员变量:code、headerBufer 和 content,它们分别表示 HTTP 响应中的状态代码、响应头和正文
public class Response implements Sendable {
//枚举类,表示状态代码
static enum Code {
OK(200, "OK"),
BAD_REQUEST(400, "Bad Request"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed");
private int number;
private String reason;
private Code(int i, String r) {
number = i;
reason =r;
}
public String toString() {
return number + " " + reason;
}
}
private Code code; //状态代码
private Content content; //响应正文
private boolean headersOnly; //表示HTTP响应中是否仅包含响应头
private ByteBuffer headerBuffer = null; //响应头
public Response(Code rc, Content c) {
this(rc, c, null);
}
public Response(Code rc, Content c, Request.Action head) {
code = rc;
content = c;
headersOnly = (head == Request.Action.HEAD);
}
/** 创建响应头的内容,把它存放到ByteBuffer */
private ByteBuffer headers() {
CharBuffer cb = CharBuffer.allocate(1024);
while(true) {
try {
cb.put("HTTP/1.1").put(code.toString()).put(CRLF);
cb.put("Server: nio/1.1").put(CRLF);
cb.put("Content-type: ") .put(content.type()).put(CRIE);
cb.put("Content-length: ").put(Long.toString(content.length())).put(CRLF);
cb.put(CRLF);
break;
} catch (BufferOverflowException x) {
assert(cb.capacity() < (1 << 16));
cb = CharBuffer.allocate(cb.capacity() * 2);
continue;
}
}
cb.flip();
return responseCharset.encode(cb); //编码
}
/** 准备 HTTP 响应中的正文以及响应头的内容 */
public void prepare() throws IOException {
content.prepare();
headerBuffer= headers();
}
/** 发送HTTP响应,如果全部发送完毕,就返回false,否则返回true */
public boolean send(ChannelIO cio) throws IOException {
if (headerBuffer == null) {
throw new IllegalStateException();
}
//发送响应头
if (headerBuffer.hasRemaining()) {
if (cio.write(headerBuffer) <= 0)
return true;
}
//发送响应正文
if (!headersOnly) {
if (content.send(cio))
return true;
}
return false;
}
/** 释放响应正文占用的资源 */
public void release() throws IOException {
content.release();
}
}
8. 代表响应正文的 Content 接口及其实现类
Response 类有一个成员变量 content,表示响应正文,它被定义为 Content 类型
public interface Content extends Sendable {
//正文的类型
String type();
//返回正文的长度
//在正文准备之前,即调用prepare()方法之前,length()方法返回“-1”
long length();
}
Content 接口继承了 Sendable 接口,Sendable 接口表示服务器端可发送给客户的内容
public interface Sendable {
// 准备发送的内容
public void prepare() throws IOException;
// 利用通道发送部分内容,如果所有内容发送完毕,就返回false
//如果还有内容未发送,就返回true
//如果内容还没有准备好,就抛出 IlleqalstateException
public boolean send(ChannelIO cio) throws IOException;
//当服务器发送内容完毕,就调用此方法,释放内容占用的资源
public void release() throws IOException;
}
Content 接口有 StringContent 和 FileContent 两个实现类,StringContent 表示字符串形式的正文,FileContent 表示文件形式的正文
FileContent 类有一个成员变量 fleChannel,它表示读文件的通道。FileContent 类的 send() 方法把 fileChannel 中的数据发送到 ChannelIO 的 SocketChannel 中,如果文件中的所有数据发送完毕,send() 方法就返回 false
public class FileContent implements Content {
//假定文件的根目录为"root",该目录应该位于classpath下
private static File ROOT = new File("root");
private File file;
public FileContent(URI uri) {
file = new File(ROOT, uri.getPath().replace('/', File,separatorChar));
}
private String type = null;
/** 确定文件类型 */
public String type() {
if (type != null) return type;
String nm = file.getName();
if (nm.endsWith(".html") || nm.endsWith(".htm"))
type = "text/html; charset=iso-8859-1"; //HTML网页
else if ((nm.indexOf('.') < 0) || nm.endsWith(".txt"))
type = "text/plain; charset=iso-8859-1"; //文本文件
else
type = "application/octet-stream"; //应用程序
return type;
}
private FileChannel fileChannel = null;
private long length = -1; //文件长度
private long position = -1;//文件的当前位置
public long length() {
return length;
}
/** 创建 FileChannel 对象 */
public void prepare() throws IOException {
if (fileChannel == null)
fileChannel = new RandomAccessFile(file, "r").getChannel();
length = fileChannel.size();
position =0;
}
/** 发送正文,如果发送完毕,就返回 false,否则返回true */
public boolean send(ChannelIO channelIO) throws IOException {
if (fileChannel == null)
throw new IllegalStateException();
if (position < 0)
throw new IllegalStateException();
if (position >= length)
return false; //如果发送完毕,就返回false
position += channelIO,transferTo(fileChannel, position, length - position);
return (position < length);
}
public void release() throws IOException {
if (fileChannel != null) {
fileChannel.close(); //关闭fileChannel
fileChannel = null;
}
}
}
Java 网络编程 —— 创建非阻塞的 HTTP 服务器的更多相关文章
- Java网络编程 -- NIO非阻塞网络编程
从Java1.4开始,为了替代Java IO和网络相关的API,提高程序的运行速度,Java提供了新的IO操作非阻塞的API即Java NIO.NIO中有三大核心组件:Buffer(缓冲区),Chan ...
- 网络编程之非阻塞connect编写
一.connect非阻塞编写 TCP连接的建立涉及到一个三次握手的过程,且socket中connect函数需要一直等到客户接收到对于自己的SYN的ACK为止才返回, 这意味着每 个connect函数总 ...
- 20145215实验五 Java网络编程及安全
20145215实验五 Java网络编程及安全 实验内容 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验步骤 本次实验我的结对编程对象是20145208蔡野,我负责编写客 ...
- 20145239杜文超 实验五 Java网络编程
20145239 实验五 Java网络编程 实验内容 组队,一人服务器,一人客户端. 下载加解密代码,先编译运行代码,一人加密一人解密,适当修改代码. 然后集成代码,一人加密后通过TCP发送,加密使用 ...
- Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO
Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...
- Java网络编程 -- BIO 阻塞式网络编程
阻塞IO的含义 阻塞(blocking)IO :阻塞是指结果返回之前,线程会被挂起,函数只有在得到结果之后(或超时)才会返回 非阻塞(non-blocking)IO :非阻塞和阻塞的概念相对应,指在不 ...
- java 网络编程复习(转)
好久没有看过Java网络编程了,现在刚好公司有机会接触,顺便的拾起以前的东西 参照原博客:http://www.cnblogs.com/linzheng/archive/2011/01/23/1942 ...
- Java 网络编程(转)
一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...
- 【IBM】Merlin 给 Java 平台带来了非阻塞 I/O
Merlin 给 Java 平台带来了非阻塞 I/O 新增的功能大幅降低了线程开销 Java 技术平台早就应该提供非阻塞 I/O 机制了.幸运的是,Merlin(JDK 1.4)有一根几乎在各个场合都 ...
- 【Java】Java网络编程菜鸟进阶:TCP和套接字入门
Java网络编程菜鸟进阶:TCP和套接字入门 JDK 提供了对 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protoco ...
随机推荐
- sdut——4541:小志志和小峰峰的日常(取石子博弈模板题 4合1)
小志志和小峰峰的日常 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 小志志和小峰峰特别喜欢一起讨论一些很好玩的问题. ...
- 4.0 SDK Workshop 纪实:一起体验多人、多屏幕共享新功能
在本月初,声网发布了 RTC Native SDK 4.0 版本.该版本提供了更高的开发灵活度,可明显提升实时场景开发效率,并让第三方插件开发更容易.上周六(8月20日),我们组织了一场小型的线下 W ...
- 全网最详细中英文ChatGPT-GPT-4示例文档-从0到1快速入门python代码解释应用——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)
目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...
- ASP.NET Core - 选项系统之选项使用
上一篇 ASP.NET Core - 选项系统之选项配置 中提到 IOptions.IOptionsMonitor 和 IOptionsSnapshot 三个接口,通过这三个接口都可以从依赖注入容器中 ...
- aspnetcore中aop的实现
aaspnetcore开发框架中实现aop不仅仅在业务上,在代码的优雅简洁和架构的稳定上都有着至关重要. 下面介绍三种用过的. 第一种通过System.Reflection的DispatchProxy ...
- 为什么HashMap查找比List快很多?
做两数之和这道题目时,引发了一个思考: 为什么两者运行时间相差如此之大???好残忍,我List比你HashMap到底差在哪**** 于是我一顿查资料.... 战犯哈希算法登场 哈希算法会根据你要存入的 ...
- 第四部分:Spdlog日志库的核心组件分析-logger
Spdlog是一个快速且可扩展的C++日志库,它支持多线程和异步日志记录.在本文中,我们将分析Spdlog日志库的核心代码,探究其实现原理和代码结构. Spdlog的基本架构 上一篇文章介绍了spdl ...
- 集合-LinkedList 源码分析(JDK 1.8)
1.概述 LinkedList 是 Java 集合框架中一个重要的实现,其底层采用的双向链表结构.和 ArrayList 一样,LinkedList 也支持空值和重复值.由于 LinkedList 基 ...
- window启动和停止服务命令
NET STOP serviceNET STOP 用于终止 Windows 服务.终止一个服务可以取消这个服务正在使用的任何一个网络连接.同样的一些服务是依赖于另外一些服务的.终止一个服务就会终止其它 ...
- ChatGPT,我彻彻底底沦陷了!
当谈到人工智能技术的时候,我们会经常听到GPT这个术语.它代表"Generative Pre-trained Transformer",是一种机器学习模型,采用了神经网络来模拟人类 ...