当前位置:   article > 正文

简单模拟webserver_模拟websocket server 工具

模拟websocket server 工具

准备知识

本章节将按照以下内容阐述

一、网络中进程之间是如何通信

二、两种通信协议:TCP、UDP

三、什么是Socket

四、模拟一个webserver

五、请求与响应

六、代码

网络中进程之间是如何通信?

通信嘛,我理解的就是两个进程之间互相收发数据,就像打电话一样。那么进程之间是如何通信的呢,可以参考博客https://www.cnblogs.com/CheeseZH/p/5264465.html

要实现进程通信,首先解决的问题就是如何唯一标识一个进程,否则通信无从谈起,我得知道你的电话号码才能给你打电话吧!那么在本地,可以通过进程PID来标识一个进程(PID熟悉吧,linux下可以使用kill -9 PID 来杀死一个进程),而在网络中如何标识一个进程呢,TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX  BSD的套接字(套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。)来实现网络进程之间的通信。

两种通信协议:TCP、UDP

TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket(理解为电话的一端)之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。

UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。(与本主题无关,了解即可)

什么是Socket?

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket,又称"套接字"。其实我理解的套接字,就是电话的一端。

Socket通讯的过程

Server端监听某个端口是否有连接请求,Client端向Server端发出连接请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

ServerSocket(服务端)编程步骤

1) 创建套接字,将套接字绑定到一个本地地址和端口上

2) 将套接字设置为监听模式,准备接受客户请求

3) 等客户请求到来时,接受连接请求,返回一个新的对应此连接的套接字,启动线程为当前的连接服务。

4) 返回,等另一个客户请求

5) 关闭套接字

6) Socket客户端编程步骤

7) 1.创建套接字

8) 2.向服务器发起请求

9) 和服务器进行通信

10) 关闭套接字

创建Socket

java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。服务端就是一个ServerSocket,这里,我们主要讲ServerSocket

ServerSocket的构造函数有:

a. ServerSocket()throws IOException

b. ServerSocket(int port)throws IOException

c. ServerSocket(int port, int backlog)throws IOException

d. ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException

参数说明:

a. port - 端口号

b. backlog - 指的是最大等待队列长度,超过这个数量的客户端链接过来就不会让它等待而是直接拒绝了

c. bindAddr -用于绑定服务器IP,InetAddress用来描述主机地址

注意:

如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。

如果设置端口为0,则系统会自动为其分配一个端口;

backlog参数将覆盖操作系统限定的队列的最大长度. 值得注意的是, 在以下几种情况中, 仍然会采用操作系统限定的队列的最大长度:

a. backlog 参数的值大于操作系统限定的队列的最大长度,

b. backlog 参数的值小于或等于0,

c. 在ServerSocket 构造方法中没有设置 backlog 参数。

常用方法:

accept()

侦听并接受到此套接字的连接。

getInetAddress()

返回此服务器套接字的本地地址。

模拟一个webserver

web server原理很简单,在服务器上运行一个ServerSocket程序,这个程序可以监控端口。比如有一个服务器的IP是10.10.40.12,其中的一个程序myserver监控的是8080端口,当浏览器发送一个请求时,这个请求里包含了所需要的页面信息index,(如: http://10.10.40.12:8080/index),请求传到服务器,myserver程序会接受这个请求,并解析请求,(因为请求就是IO流,所以可以用JAVA中的IO操作来解析),解析后,找到请求所需要的页面,页面其实就是一个文件,用JAVA的文件操作,读出这个页面,然后通过Socket写到客户端,就可以了。

简单来说访问网站的过程是这个:用户在浏览器输入一个地址,摁回车之后,就会向服务器发送一个请求,告诉服务器它想要一个页面,服务器找到这个页面,然后给浏览器发送回来。

请求与响应

请求,当客户端发起请求时,我的理解就是客户端向浏览器发送了个字符串,这个字符串包含了一些信息,比如:

  POST /examples/default.jsp HTTP/1.0

  Accept: text/plain;text/html

  Accept-Language: en-gb

  Connection:Keep-Alive

  Host: localhost

  User-Agent: Mozilla/4.0(compatible;MSIE:4.01;Widows 98)

  Content-Length: 33

  Content-Type:application/x-www-form-urlencoded

  Accept-Encodding:gzip,deflate

  lastName=Franks&firstName=Michael

第一行是请求方法 请求资源路径 协议/协议版本,中间那部分是请求头,最后一行是请求体,这里用的POST方法,所以表单的参数会在请求体中,这行和上面的用一个空行隔开,以便解析此请求的时候能知道往下就是请求体了。

响应。服务器接收到用户的请求之后,会找到用户所要的资源(页面,图片等),再发送到客户端。响应的格式如下:

  HTTP/1.1 200 OK

  Server: Microsoft-IIS/4.0

  Date: Mon, 5 Jan 2004 13:13:33 GMT

  Content-Type: text/html

  Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT

  Content-Length: 112

 

  <html>

  <head>

  <title>HTTP Response Example</title>

  </head>

  <body>

  Welcome to Software

  </body>

  </html>

第一行的意思:协议/协议版本  状态码(成功,失败等状态用数字来表示)  状态描述,接下来是告诉客户端的一些信息,再接下来就是客户请求的页面的内容。

 

下面写代码

先看一下流程

从图中看出我们需要创建以下类来完成:

1. HttpServer 用来创建ServerSocket

2. Dispatcher 用来分发请求

3. Reqest 用来获取请求信息

4. Response 用来向客户端发送响应信息

另外,为了方便,我们还需要些一个Util类,用来写一些工具

注意:从Socket获取的inputstream/outputStream 执行关闭操作时,Socket也会关闭,所以我们要在inputStream和outputStream都完成工作时,一起关闭。

  1. HttpServer:
  2. package com.cn.webserver;
  3. import java.io.IOException;
  4. import java.net.InetAddress;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8. import java.util.concurrent.ExecutorService;
  9. import java.util.concurrent.Executors;
  10. public class HttpServer {
  11. public boolean shutdown = false;
  12. public static void main(String[] args) {
  13. try {
  14. new HttpServer().start();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. public void start() throws IOException {
  20. int port = 8081;
  21. int backlog = 1000;
  22. ServerSocket serverSocket = null;
  23. try {
  24. serverSocket = new ServerSocket(port, backlog,InetAddress.getByName("127.0.0.1"));
  25. while (!shutdown) {
  26. Socket socket = serverSocket.accept();
  27. ExecutorService es = Executors.newCachedThreadPool();
  28. es.execute(new Dispatcher(socket));
  29. }
  30. } catch (IOException e) {
  31. shutdown = true;
  32. serverSocket.close();
  33. e.printStackTrace();
  34. }
  35. }
  36. }
  37. Dispatcher:
  38. package com.cn.webserver;
  39. import java.io.IOException;
  40. import java.io.InputStream;
  41. import java.io.OutputStream;
  42. import java.net.Socket;
  43. import com.cn.request.Request;
  44. import com.cn.response.Response;
  45. import com.cn.util.Util;
  46. public class Dispatcher implements Runnable{
  47. private Socket socket;
  48. private InputStream is;
  49. private OutputStream os;
  50. private int code = 200;
  51. public Dispatcher(Socket socket){
  52. this.socket = socket;
  53. try {
  54. this.is = socket.getInputStream();
  55. this.os = socket.getOutputStream();
  56. } catch (IOException e) {
  57. code = 500;
  58. e.printStackTrace();
  59. }
  60. }
  61. @Override
  62. public void run() {
  63. try {
  64. Request res = new Request(is);
  65. Response resp = new Response(os);
  66. resp.push(res.getUrl(), code);
  67. } catch (Exception e) {
  68. // TODO Auto-generated catch block
  69. e.printStackTrace();
  70. }finally{
  71. Util.closeIO(is);
  72. Util.closeIO(os);
  73. }
  74. }
  75. }
  76. Request:
  77. package com.cn.request;
  78. import java.io.IOException;
  79. import java.io.InputStream;
  80. import java.io.InputStreamReader;
  81. import java.io.Reader;
  82. import java.util.HashMap;
  83. import java.util.Map;
  84. import com.cn.util.Util;
  85. public class Request {
  86. private InputStream in;
  87. private Map parameterMap;
  88. private String url;
  89. private String method;
  90. public Request(InputStream in){
  91. this.in = in;
  92. this.parameterMap = new HashMap();
  93. parse();
  94. }
  95. private void parse() {
  96. String requestHead = read();
  97. if(requestHead==null||requestHead.length()<=0){
  98. return ;
  99. }
  100. String firstLine = requestHead.substring(0, requestHead.indexOf("\r\n"));
  101. int a = requestHead.indexOf("/");
  102. this.method = requestHead.substring(0, a).trim();
  103. String urlparam = null;
  104. String parameter = null;
  105. urlparam = firstLine.substring(a,requestHead.indexOf("HTTP/"));
  106. if(method.equalsIgnoreCase("post")){
  107. parameter = requestHead.substring(requestHead.lastIndexOf("\r\n"));
  108. this.url = urlparam;
  109. }else{
  110. if(firstLine.contains("?")){
  111. String []params = urlparam.split("[?]");
  112. this.url = params[0];
  113. if(params.length>0){
  114. String []params1 = params[1].split("&");
  115. if(params1.length>0){
  116. String[] param;
  117. String key = null;
  118. String value = null;
  119. for(int i = 0; i < params1.length;i++){
  120. param = params1[i].split("=");
  121. if(param.length>0){
  122. key = param[0];
  123. if(param.length>1){
  124. value = param[1];
  125. }else{
  126. value = null;
  127. }
  128. this.parameterMap.put(key, value);
  129. }
  130. }
  131. }
  132. }
  133. }else{
  134. this.url=urlparam;
  135. }
  136. }
  137. }
  138. /**
  139. * 获取请求信息
  140. * @return
  141. */
  142. private String read() {
  143. StringBuffer sb = new StringBuffer();
  144. Reader re = null;
  145. try {
  146. re = new InputStreamReader(this.in, "UTF-8");
  147. char []cbuf = new char[20480];
  148. int index = 0;
  149. index = re.read(cbuf);
  150. if(index>0){
  151. sb.append(cbuf,0,index);
  152. }
  153. } catch (Exception e) {
  154. e.printStackTrace();
  155. }
  156. return sb.toString();
  157. }
  158. public String getMethod() {
  159. return method;
  160. }
  161. public void setMethod(String method) {
  162. this.method = method;
  163. }
  164. public String getParameterMap(String key) {
  165. return (String)parameterMap.get(key);
  166. }
  167. public void setParameterMap(Map parameterMap) {
  168. this.parameterMap = parameterMap;
  169. }
  170. public String getUrl() {
  171. return url;
  172. }
  173. public void setUrl(String url) {
  174. this.url = url;
  175. }
  176. }
  177. Response:
  178. package com.cn.response;
  179. import java.io.BufferedReader;
  180. import java.io.BufferedWriter;
  181. import java.io.File;
  182. import java.io.FileInputStream;
  183. import java.io.FileNotFoundException;
  184. import java.io.FileReader;
  185. import java.io.IOException;
  186. import java.io.InputStreamReader;
  187. import java.io.OutputStream;
  188. import java.io.OutputStreamWriter;
  189. import java.util.Date;
  190. import com.cn.util.Util;
  191. public class Response {
  192. private OutputStream out;
  193. private int code;
  194. private StringBuffer context ;
  195. public Response(OutputStream out){
  196. this.out = out;
  197. context = new StringBuffer();
  198. }
  199. public void push(String url,int code){
  200. this.read(url);
  201. String head = this.createHead(context.length());
  202. try {
  203. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(this.out,"UTF-8"));
  204. bw.append(head);
  205. bw.append(this.context);
  206. bw.flush();
  207. } catch (IOException e) {
  208. e.printStackTrace();
  209. }
  210. }
  211. private void read(String url){
  212. if(url==null||url.length()<0){
  213. return;
  214. }
  215. url = url.substring(1);
  216. url = System.getProperty("user.dir")+File.separatorChar+"WebRoot"+File.separatorChar+url;
  217. File file = new File(url);
  218. BufferedReader br = null;
  219. if(!file.exists()){
  220. this.code =404;
  221. this.context.append("<h1>大写的<span style='font-size:90px'>404</span></h1>");
  222. }else{
  223. try {
  224. br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
  225. String cacheStr = br.readLine();
  226. while(cacheStr!=null){
  227. context.append(cacheStr);
  228. cacheStr = br.readLine();
  229. }
  230. } catch (FileNotFoundException e) {
  231. this.code =404;
  232. e.printStackTrace();
  233. } catch (IOException e){
  234. e.printStackTrace();
  235. }finally{
  236. Util.closeIO(br);
  237. }
  238. }
  239. }
  240. private String createHead(int len){
  241. StringBuffer headInfo = new StringBuffer();
  242. headInfo.append("HTTP/1.1").append(" ").append(code).append(" ");
  243. switch (code) {
  244. case 200:
  245. headInfo.append("ok");
  246. break;
  247. case 404:
  248. headInfo.append("NOT FOUND");
  249. break;
  250. case 505:
  251. headInfo.append("SERVER ERROR");
  252. break;
  253. }
  254. headInfo.append("\r\n");
  255. //响应头
  256. headInfo.append("Server: Webserver").append("\r\n");
  257. headInfo.append("Date:").append(new Date()).append("\r\n");
  258. headInfo.append("content-type:text/html;charset=UTF-8").append("\r\n");
  259. //正文长度:字节长度
  260. headInfo.append("Content-Length:").append(len).append("\r\n");
  261. //分隔符
  262. headInfo.append("\r\n");
  263. return headInfo.toString();
  264. }
  265. }
  266. Util:
  267. package com.cn.util;
  268. import java.io.Closeable;
  269. import java.io.IOException;
  270. public class Util {
  271. public static <T extends Closeable> void closeIO(T ...ios){
  272. for(Closeable c :ios){
  273. try {
  274. if(c!=null){
  275. c.close();
  276. }
  277. } catch (IOException e) {
  278. e.printStackTrace();
  279. }
  280. }
  281. }
  282. }

 

运行效果:

 

 

 页面不存在的情况:

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/216996
推荐阅读
相关标签
  

闽ICP备14008679号