如何写一个简单的HTTP服务器

如何写一个简单的HTTP服务器,第1张

本Guide利用Eclipse及Ant建立简单Web Service演示Web Service基本发程:

1.系统条件:

Eclipse Java EE IDE for Web Developers

Java SE 6

Windows XP

2.基本环境搭建:

1)Java SE6 JDK安装:载Java SE6 JDK双击安装默认选项进行安装即

2)Eclipse安装与配置:

安装直接解压

配置处两点Window>Preferences>Java>Installed JREs确保设置:

安装路径能略同

Window>Preferences>Java>Compiler 确保设置:

3.建立Server端工程相关包与类:

创建Java Project命名wsServerHelloWorld:

项目建立包:org.gnuhpc.wsServer

包边建立类:SayHello

SayHello.java文件输入代码

package org.gnuhpc.wsServer

import javax.jws.WebService

@WebService

public class SayHello {

private static final String SALUTATION = "Hello"

public String getGreeting(String name) {

return SALUTATION + " " + name

}

}

其注意@WebService 称作annotation或者metadataJava SE 5Web Services Metadata Specification引入Java SE 6于Web Services规范升级及JAX-WS(Java API for XML Web Services)2.0规范些升级使我Web Services创建调用变更加容易使用些新功能我仅仅使用简单Annotations注释Java类创建Web Services发者其类前用该annotations指定类告诉runtime engineWeb Service式操作使能该类annotations产布署Web ServiceWSDL映射annotationsJava源代码与代表Web ServiceWSDL元素连接起

4.使用Ant产Server端代码:

首先项目新建文件:build.xml使用OpenWith>AntEditor打输入脚本代码:

<arg line="-cp ./bin -keep -s ./src -d ./bin

org.gnuhpc.wsServer.SayHello"/>

default指定默认执行Targetwsgen,wsgen创建能够使用WebService类所用于WebService发布源代码文件经编译二进制类文件WSDL符合规范该类WebService

Target名称wsgen具体执行命令参数:

-cp 类路径

-keep产java文件

-s 产源文件放哪

-d 产输问价放哪

使用Ant Build选项运行:

功执行提示我刷新Project

我Project区域看结:

5.布Web Service

org.gnuhpc.wsServer建立类RunService:

package org.gnuhpc.wsServer

import javax.xml.ws.Endpoint

public class RunService {

/**

*@paramargs

*/

public static void main(String[] args) {

System.out.println("SayHello Web Service started.")

Endpoint.publish("" ,

new SayHello())

}

}

运行Run As>Java Application我结说明web serviceServer端已经启

6.查看WSDL:

Window>Show View>Other>General>Internal Web Browser其输入:?wsdl

看看底WSDL都记录哪些信息看完停止该Server

7.监测Server

我创建完Server用Eclipse Web Services Explorer监测Server

Window>Open Perspective>Other >JavaEE

打Eclipse Web Services Explorer

点击右角WSDL Page按钮:

单击WSDL MainURL输入:?wsdl 按Go按钮现视图:

我触发Web Service操作:点击getGreetings添加参数比gnuhpc点击Go按钮:

8.创建Client端 工程相关包与类:

创建Java Project命名wsClientHelloWorld项目建立包:org.gnuhpc.wsClient

9.使用Ant产Client代码框架:

编写Web service使用工具利用WSDL进行调用客户端桩;或者使用底层API手编写Web service前者便者灵现我通前者做说明:

新建文件build.xml

New>File>build.xml

<arg line="-keep -s ./src -p org.gnuhpc.wsClient

-d ./bin ?wsdl"/>

注意:wsgen 支持 Java class 创建 Web serviceswsimport 支持 WSDL 创建 Web services别应于 JAX-RPC 式 Java2WSDL WSDL2Java要根据发布WSDL进行创建要先运行RunServer原

运行ServerRunService : Run As>Java Application>

运行该Ant脚本产Client代码:Run As>Ant Build

运行功提示:

代码:

步读取WSDL并客户端桩些桩我代码所用Java类接口些桩给服务器端功能提供客户端接口例我服务器提供Maths服务该服务带叫做add我客户端代码调用桩桩实现该使用参数封装Java调用变Webservice请求请求基于HTTP发送给服务器且使用SOAP作RPC协议监听服务器接收该SOAP消息(十八九)其转换服务器处调用

需要socket(套接字)编程、HTTP协议的知识,实现一个最简单的HTTP服务器,其实就是根据HTTP协议的通信规则来发送socket数据,往往是绑定(用bind调用)一个socket(套接字)然后监听(用listen调用)80端口,具体任何实现可以参考网上的一些简单的代码。之所以说这是最简单的HTTP服务器,是因为HTTP服务器还要处理并发、缓存等功能,更进一步还要支持某种动态脚本比如PHP的模块。

1.能接受HttpRequest并返回HttpResponse

2.满足一个Server的基本特征,能够长时间运行

关于Http协议一般HttpServer都会声明支持Http协议的哪些特性,nanohttpd作为一个轻量级的httpserver只实现了最简单、最常用的功能,不过我们依然可以从中学习很多。

首先看下NanoHttpd类的start函数

[java] view plaincopy

public void start() throws IOException {

myServerSocket = new ServerSocket()

myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort))

myThread = new Thread(new Runnable() {

@Override

public void run() {

do {

try {

final Socket finalAccept = myServerSocket.accept()

registerConnection(finalAccept)

finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT)

final InputStream inputStream = finalAccept.getInputStream()

asyncRunner.exec(new Runnable() {

@Override

public void run() {

OutputStream outputStream = null

try {

outputStream = finalAccept.getOutputStream()

TempFileManager tempFileManager = tempFileManagerFactory.create()

HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress())

while (!finalAccept.isClosed()) {

session.execute()

}

} catch (Exception e) {

// When the socket is closed by the client, we throw our own SocketException

// to break the "keep alive" loop above.

if (!(e instanceof SocketException &&"NanoHttpd Shutdown".equals(e.getMessage()))) {

e.printStackTrace()

}

} finally {

safeClose(outputStream)

safeClose(inputStream)

safeClose(finalAccept)

unRegisterConnection(finalAccept)

}

}

})

} catch (IOException e) {

}

} while (!myServerSocket.isClosed())

}

})

myThread.setDaemon(true)

myThread.setName("NanoHttpd Main Listener")

myThread.start()

}

1.创建ServerSocket,bind制定端口

2.创建主线程,主线程负责和client建立连接

3.建立连接后会生成一个runnable对象放入asyncRunner中,asyncRunner.exec会创建一个线程来处理新生成的连接。

4.新线程首先创建了一个HttpSession,然后while(true)的执行httpSession.exec。

这里介绍下HttpSession的概念,HttpSession是java里Session概念的实现,简单来说一个Session就是一次httpClient->httpServer的连接,当连接close后session就结束了,如果没结束则session会一直存在。这点从这里的代码也能看到:如果socket不close或者exec没有抛出异常(异常有可能是client段断开连接)session会一直执行exec方法。

一个HttpSession中存储了一次网络连接中server应该保存的信息,比如:URI,METHOD,PARAMS,HEADERS,COOKIES等。

5.这里accept一个client的socket就创建一个独立线程的server模型是ThreadServer模型,特点是一个connection就会创建一个thread,是比较简单、常见的socket server实现。缺点是在同时处理大量连接时线程切换需要消耗大量的资源,如果有兴趣可以了解更加高效的NIO实现方式。

当获得client的socket后自然要开始处理client发送的httprequest。

Http Request Header的parse:

[plain] view plaincopy

// Read the first 8192 bytes.

// The full header should fit in here.

// Apache's default header limit is 8KB.

// Do NOT assume that a single read will get the entire header at once!

byte[] buf = new byte[BUFSIZE]

splitbyte = 0

rlen = 0

{

int read = -1

try {

read = inputStream.read(buf, 0, BUFSIZE)

} catch (Exception e) {

safeClose(inputStream)

safeClose(outputStream)

throw new SocketException("NanoHttpd Shutdown")

}

if (read == -1) {

// socket was been closed

safeClose(inputStream)

safeClose(outputStream)

throw new SocketException("NanoHttpd Shutdown")

}

while (read >0) {

rlen += read

splitbyte = findHeaderEnd(buf, rlen)

if (splitbyte >0)

break

read = inputStream.read(buf, rlen, BUFSIZE - rlen)

}

}

1.读取socket数据流的前8192个字节,因为http协议中头部最长为8192

2.通过findHeaderEnd函数找到header数据的截止位置,并把位置保存到splitbyte内。

[java] view plaincopy

if (splitbyte <rlen) {

inputStream.unread(buf, splitbyte, rlen - splitbyte)

}

parms = new HashMap<String, String>()

if(null == headers) {

headers = new HashMap<String, String>()

}

1.http协议规定header和body之间使用两个回车换行分割

1.Http协议第一行是Method URI HTTP_VERSION

2.后面每行都是KEY:VALUE格式的header

3.uri需要经过URIDecode处理后才能使用

4.uri中如果包含?则表示有param,httprequest的param一般表现为:/index.jsp?username=xiaoming&id=2

下面是处理cookie,不过这里cookie的实现较为简单,所以跳过。之后是serve方法,serve方法提供了用户自己实现httpserver具体逻辑的很好接口。在NanoHttpd中的serve方法实现了一个默认的简单处理功能。

[java] view plaincopy

发送response的步骤如下:

1.设置mimeType和Time等内容。

2.创建一个PrintWriter,按照HTTP协议依次开始写入内容

3.第一行是HTTP的返回码

4.然后是content-Type

5.然后是Date时间

6.之后是其他的HTTP Header

7.设置Keep-Alive的Header,Keep-Alive是Http1.1的新特性,作用是让客户端和服务器端之间保持一个长链接。

8.如果客户端指定了ChunkedEncoding则分块发送response,Chunked Encoding是Http1.1的又一新特性。一般在response的body比较大的时候使用,server端会首先发送response的HEADER,然后分块发送response的body,每个分块都由chunk length\r\n和chunk data\r\n组成,最后由一个0\r\n结束。

9.如果没指定ChunkedEncoding则需要指定Content-Length来让客户端指定response的body的size,然后再一直写body直到写完为止。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/276265.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-20
下一篇2023-04-20

发表评论

登录后才能评论

评论列表(0条)

    保存