赞
踩
本章节将按照以下内容阐述
一、网络中进程之间是如何通信
二、两种通信协议: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是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket(理解为电话的一端)之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。(与本主题无关,了解即可)
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个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) 关闭套接字
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()
返回此服务器套接字的本地地址。
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都完成工作时,一起关闭。
- HttpServer:
-
- package com.cn.webserver;
-
-
-
- import java.io.IOException;
-
- import java.net.InetAddress;
-
- import java.net.ServerSocket;
-
- import java.net.Socket;
-
- import java.net.UnknownHostException;
-
- import java.util.concurrent.ExecutorService;
-
- import java.util.concurrent.Executors;
-
-
-
- public class HttpServer {
-
- public boolean shutdown = false;
-
-
-
- public static void main(String[] args) {
-
- try {
-
- new HttpServer().start();
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
-
- }
-
-
-
- public void start() throws IOException {
-
- int port = 8081;
-
- int backlog = 1000;
-
- ServerSocket serverSocket = null;
-
- try {
-
- serverSocket = new ServerSocket(port, backlog,InetAddress.getByName("127.0.0.1"));
-
- while (!shutdown) {
-
- Socket socket = serverSocket.accept();
-
- ExecutorService es = Executors.newCachedThreadPool();
-
- es.execute(new Dispatcher(socket));
-
- }
-
- } catch (IOException e) {
-
- shutdown = true;
-
- serverSocket.close();
-
- e.printStackTrace();
-
- }
-
- }
-
- }
-
- Dispatcher:
-
- package com.cn.webserver;
-
- import java.io.IOException;
-
- import java.io.InputStream;
-
- import java.io.OutputStream;
-
- import java.net.Socket;
-
- import com.cn.request.Request;
-
- import com.cn.response.Response;
-
- import com.cn.util.Util;
-
-
-
- public class Dispatcher implements Runnable{
-
- private Socket socket;
-
- private InputStream is;
-
- private OutputStream os;
-
- private int code = 200;
-
- public Dispatcher(Socket socket){
-
- this.socket = socket;
-
- try {
-
- this.is = socket.getInputStream();
-
- this.os = socket.getOutputStream();
-
- } catch (IOException e) {
-
- code = 500;
-
- e.printStackTrace();
-
- }
-
- }
-
- @Override
-
- public void run() {
-
- try {
-
- Request res = new Request(is);
-
- Response resp = new Response(os);
-
- resp.push(res.getUrl(), code);
-
- } catch (Exception e) {
-
- // TODO Auto-generated catch block
-
- e.printStackTrace();
-
- }finally{
-
- Util.closeIO(is);
-
- Util.closeIO(os);
-
- }
-
- }
-
- }
-
-
-
- Request:
-
- package com.cn.request;
-
-
-
- import java.io.IOException;
-
- import java.io.InputStream;
-
- import java.io.InputStreamReader;
-
- import java.io.Reader;
-
- import java.util.HashMap;
-
- import java.util.Map;
-
-
-
- import com.cn.util.Util;
-
-
-
- public class Request {
-
- private InputStream in;
-
- private Map parameterMap;
-
- private String url;
-
- private String method;
-
- public Request(InputStream in){
-
- this.in = in;
-
- this.parameterMap = new HashMap();
-
- parse();
-
- }
-
- private void parse() {
-
- String requestHead = read();
-
- if(requestHead==null||requestHead.length()<=0){
-
- return ;
-
- }
-
- String firstLine = requestHead.substring(0, requestHead.indexOf("\r\n"));
-
- int a = requestHead.indexOf("/");
-
- this.method = requestHead.substring(0, a).trim();
-
- String urlparam = null;
-
- String parameter = null;
-
- urlparam = firstLine.substring(a,requestHead.indexOf("HTTP/"));
-
- if(method.equalsIgnoreCase("post")){
-
- parameter = requestHead.substring(requestHead.lastIndexOf("\r\n"));
-
- this.url = urlparam;
-
- }else{
-
- if(firstLine.contains("?")){
-
- String []params = urlparam.split("[?]");
-
- this.url = params[0];
-
- if(params.length>0){
-
- String []params1 = params[1].split("&");
-
- if(params1.length>0){
-
- String[] param;
-
- String key = null;
-
- String value = null;
-
- for(int i = 0; i < params1.length;i++){
-
- param = params1[i].split("=");
-
- if(param.length>0){
-
- key = param[0];
-
- if(param.length>1){
-
- value = param[1];
-
- }else{
-
- value = null;
-
- }
-
- this.parameterMap.put(key, value);
-
- }
-
- }
-
- }
-
- }
-
- }else{
-
- this.url=urlparam;
-
- }
-
- }
-
- }
-
- /**
- * 获取请求信息
- * @return
- */
-
- private String read() {
-
- StringBuffer sb = new StringBuffer();
-
- Reader re = null;
-
- try {
-
- re = new InputStreamReader(this.in, "UTF-8");
-
- char []cbuf = new char[20480];
-
- int index = 0;
-
- index = re.read(cbuf);
-
- if(index>0){
-
- sb.append(cbuf,0,index);
-
- }
-
- } catch (Exception e) {
-
- e.printStackTrace();
-
- }
-
- return sb.toString();
-
- }
-
- public String getMethod() {
-
- return method;
-
- }
-
- public void setMethod(String method) {
-
- this.method = method;
-
- }
-
- public String getParameterMap(String key) {
-
- return (String)parameterMap.get(key);
-
- }
-
- public void setParameterMap(Map parameterMap) {
-
- this.parameterMap = parameterMap;
-
- }
-
- public String getUrl() {
-
- return url;
-
- }
-
- public void setUrl(String url) {
-
- this.url = url;
-
- }
-
- }
-
- Response:
-
- package com.cn.response;
-
-
-
- import java.io.BufferedReader;
-
- import java.io.BufferedWriter;
-
- import java.io.File;
-
- import java.io.FileInputStream;
-
- import java.io.FileNotFoundException;
-
- import java.io.FileReader;
-
- import java.io.IOException;
-
- import java.io.InputStreamReader;
-
- import java.io.OutputStream;
-
- import java.io.OutputStreamWriter;
-
- import java.util.Date;
-
-
-
- import com.cn.util.Util;
-
-
-
- public class Response {
-
- private OutputStream out;
-
- private int code;
-
- private StringBuffer context ;
-
- public Response(OutputStream out){
-
- this.out = out;
-
- context = new StringBuffer();
-
- }
-
- public void push(String url,int code){
-
- this.read(url);
-
- String head = this.createHead(context.length());
-
- try {
-
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(this.out,"UTF-8"));
-
- bw.append(head);
-
- bw.append(this.context);
-
- bw.flush();
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
-
- }
-
- private void read(String url){
-
- if(url==null||url.length()<0){
-
- return;
-
- }
-
- url = url.substring(1);
-
- url = System.getProperty("user.dir")+File.separatorChar+"WebRoot"+File.separatorChar+url;
-
- File file = new File(url);
-
- BufferedReader br = null;
-
- if(!file.exists()){
-
- this.code =404;
-
- this.context.append("<h1>大写的<span style='font-size:90px'>404</span></h1>");
-
- }else{
-
- try {
-
- br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
-
- String cacheStr = br.readLine();
-
- while(cacheStr!=null){
-
- context.append(cacheStr);
-
- cacheStr = br.readLine();
-
- }
-
- } catch (FileNotFoundException e) {
-
- this.code =404;
-
- e.printStackTrace();
-
- } catch (IOException e){
-
- e.printStackTrace();
-
- }finally{
-
- Util.closeIO(br);
-
- }
-
- }
-
- }
-
- private String createHead(int len){
-
- StringBuffer headInfo = new StringBuffer();
-
- headInfo.append("HTTP/1.1").append(" ").append(code).append(" ");
-
- switch (code) {
-
- case 200:
-
- headInfo.append("ok");
-
- break;
-
- case 404:
-
- headInfo.append("NOT FOUND");
-
- break;
-
- case 505:
-
- headInfo.append("SERVER ERROR");
-
- break;
-
- }
-
- headInfo.append("\r\n");
-
- //响应头
-
- headInfo.append("Server: Webserver").append("\r\n");
-
- headInfo.append("Date:").append(new Date()).append("\r\n");
-
- headInfo.append("content-type:text/html;charset=UTF-8").append("\r\n");
-
- //正文长度:字节长度
-
- headInfo.append("Content-Length:").append(len).append("\r\n");
-
- //分隔符
-
- headInfo.append("\r\n");
-
- return headInfo.toString();
-
- }
-
- }
-
- Util:
-
- package com.cn.util;
-
-
-
- import java.io.Closeable;
-
- import java.io.IOException;
-
-
-
- public class Util {
-
- public static <T extends Closeable> void closeIO(T ...ios){
-
- for(Closeable c :ios){
-
- try {
-
- if(c!=null){
-
- c.close();
-
- }
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
-
- }
-
- }
-
- }
运行效果:
页面不存在的情况:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。