前言
在我们日常开发中,尤其是从事web开发的童鞋们,一些基础网络知识是必备的。平时大家获取都是碎片化的知识,为此我进行了系统性的总结。
本文对网络应用层通信进行了总结,看完后大家会对应用间的通信有进一步的认识,实乃居家旅行必备好文,希望可以帮助到大家。
快速索引
一、万维网(World Wide Web)(B/S架构)
一切都要从万维网的诞生讲起:
万维网WWW 也称为Web、3W等。
是一个分布式的超媒体(图形,声音,动画等)系统,是超文本(文本信息)系统的扩充。
WWW是基于客户机/服务器方式的 。客户程序向服务器程序发送请求,服务器程序向客户程序送回客户要的万维网文档。
若想标志定位分布在整个互联网的万维网文档,就需要使用URL(统一资源定位符)
二、URL(Uniform Resource Locator)
URL表示从互联网上得到的资源位置和访问这些资源的方法。
URL格式(想必大家都很熟)
<协议>://<域名>:<端口>/<路径>
协议就是用来实现万维网上的链接,最常用的就是HTTP协议了,还有少数网站使用加密的HTTPs协议。
三、HTTP 超文本传送协议
终于到了重头戏,应用层协议HTTP。
它存在的意义就是解决浏览器怎样向万维网服务器请求万维网文档。
一张图说明HTTP包传送:
万维网的工作过程:
- 每个网点都有一个服务器进程不断地监听TCP 80 端口;
- 浏览器发出建立连接请求,服务器收到请求建立TCP连接;
- 浏览器向服务器提出浏览某个万维网页面的请求,浏览器返回所请求的页面做响应;
- 释放TCP连接。
http协议特点:
- Http使用面向连接的TCP运输层协议,保证了数据的可靠传输。但http是无连接的(交给运输层协议解决)。
- Http协议是无状态的,表示每次请求响应过程都是全新的,这样的特性简化了服务器的设计,使得服务器能更容易支持大量并发的http请求(从而催生了cookie)。
- Http是面向事务的应用层协议。
HTTP/1.0和HTTP/1.1
HTTP/1.0
- HTTP协议先和服务器建立TCP连接,需要三次握手。
- 建立TCP连接前两次报文握手完成后(一个RTT),客户端把HTTP请求报文作为建立TCP第三次报文数据发送给Server。
- 服务器收到请求,把文档作为相应报文返回。
图示HTTP/1.0 工作:
请求一个万维网文档所需时间(T)= 2 * RTT + 文档传输时间 ;
特点:
- 时间开销大。若一个主页上有很多链接的对象需要依次链接,那这个时间开销是很大的(通常一个页面有多个请求响应);
- 非持续链接。每一次连接都需要分配缓存和变量,服务器压力大。
HTTP/1.1
HTTP/1.1 则很好的解决了问题。
其特点:
- 持续连接。即服务器在完成响应后仍然保持TCP连接一段时间,可以继续在此连接上传送后续HTTP报文;
- 传送方式有两种:
- 非流水线方式。客户在收到前一次响应后再发送请求
- 流水线方式。客户端在未收到响应前就发送一系列请求,服务器收到请求就持续性的发送响应报文(自然这种方式更具有效率)。
HTTP报文结构
HTTP请求报文和响应报文都是由三部分构成:
- 开始行。
- 请求行:包括 请求方法 | URL | HTTP协议版本;
- 状态行:包括 HTTP协议版本 | 状态码 | 状态解释短语;
- 首部行。用来说明浏览器、服务器和报文主体。
- 实体主体。请求报文一般不用,响应报文也可能没有。
访问百度首页的Network:
Http请求报文的方法:
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
7 | OPTIONS | 允许客户端查看服务器的性能。 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
最常用的就是 GET 和 POST 方法
状态码
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用;
下面是常见的HTTP状态码:
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
Cookie
万维网用户可以根据Cookie来追踪用户,客户端和服务端通过cookie来传递状态信息。
其在实际网络是怎样工作的呢?
- 用户A第一次浏览使用Cookie的网站时,该网站服务器就为用户A产生一个唯一的识别码,并以此作为索引在数据库中产生一个项目。在响应报文中添加一个Set-cookie的首部行(上文中访问百度的响应头有标明);
Set-cookin:31d4dk4535ff567
- A收到响应,浏览器就在它管理的特定Cookie文件中添加一行。
- A用户再浏览此网站时,浏览器就从特定文件中取出cookie,并把cookie放到每一次的请求报文的首部行中。 服务器就能识别用户A,从而提供个性化服务。
Cookie:31d4dk4535ff567
Session
概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。
session与Cookie的区别:
1. session存储数据在服务器端,Cookie在客户端
2. session没有数据大小限制,Cookie有
3. session数据安全,Cookie相对于不安全
四、应用进程跨网络的通信(C/S架构)
日常网络使用中,我们经常使用应用来与服务器通信,那么具体过程是怎样的呢?
大多数操作系统使用系统调用的机制在应用程序和操作系统之间传递控制权。
当某个应用进程启动系统调用是时,控制权就从应用进程传递给了系统调用接口。此接口再把控制权传递给操作系统。操作系统把这个调用转给某个内部过程,并执行请求的操作。
目前使用最广泛的可供应用程序使用的TCP/IP的应用编程接口API就是套接字接口(socket interface)(或插口接口)。
网络编程(java)
在讨论网络编程时,常常把套接字(socket)作为应用进程和运输层之间的接口。
UDP情况也是类似的,只是UDP是无连接的,通信两端也可以用套接字表示。
套接字(Socket)是应用进程为了获取网络的通信服务而与操作系统进行交互使用的一种机制。
有了背景知识,我们看看在java中网络编程中是怎样操作的吧!
TCP方式通信
TCP协议是面向连接的,是可靠的运输层协议。
操作步骤:
- 当然是先建立连接,创建
socket
实例。在网络编程中,一定是Client端 和Server端 同时编写,两者需要实时通信; - 利用IO流进行数据传输;
- 释放连接,调用
close()
方法。
代码永远最直观,最有效,接下来我们一起欣赏helloworld():
TCP Server 端
/* 范例名称:简单的client/server程序
* 源文件名称:TestClient.java/TestServer.java
* 要 点:
* 1. Java Socket编程步骤
* 2. Socket/ServerSocket类用法
* 3. 通过Socket对象可以获取通信对方Socket的信息
*/
import java.net.*;
import java.io.*;
public class TestServer {
public static void main(String args[]) {
try {
//创建ServerSocket实例对象,指定TCP监听端口
ServerSocket s = new ServerSocket(8888);
//Server 端对服务进行了分离,主服务器进程负责监听,接受 连接请求,一旦接受请求,就创建从属服务器进程负责处理连接
while (true) {
//接受Client的连接请求,返回Server端socket对象,建立连接(s1负责)
Socket s1 = s.accept();
//获取Server端socket对象的IO流,进行数据传输
OutputStream os = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("Hello," + s1.getInetAddress() +
"port#" +s1.getPort() + " bye-bye!");
//关闭流
dos.close();
//释放连接
s1.close();
}
}catch (IOException e) {
e.printStackTrace();
System.out.println("程序运行出错:" + e);
}
}
}
TCP Client 端
/* 范例名称:简单的client/server程序
* 源文件名称:TestClient.java/TestServer.java
* 要 点:
* 1. Java Socket编程步骤
* 2. Socket/ServerSocket类用法
* 3. 通过Socket对象可以获取通信对方Socket的信息
*/
import java.net.*;
import java.io.*;
public class TestClient {
public static void main(String args[]) {
try {
//建立连接,创建Socket实例,指定server IP,和TCP端口
Socket s1 = new Socket("127.0.0.1", 8888);
//获取Socket实例的流
InputStream is = s1.getInputStream();
DataInputStream dis = new DataInputStream(is);
System.out.println(dis.readUTF());
//关闭流
dis.close();
//释放TCP连接
s1.close();
} catch (ConnectException connExc) {
connExc.printStackTrace();
System.err.println("服务器连接失败!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP方式通信
UDP是无连接的,所以将数据打包传输;
在UDP传输中,并没有明确的客户端服务端概念,两者的地位是相同的,所做的动作也是相同的。
操作步骤:
- 创建套接字DatagramSocket,创建数据包;
- 数据需要打包成字节数组,放到数据包中;
- 套接字将数据包发送到指定主机端口。
- 释放套接字
UDP Client 端
import java.net.*;
import java.io.*;
public class TestUDPClient
{
public static void main(String args[]) throws Exception
{
long n = 10000L;
//long类型数据 利用流转换成字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(n);
byte[] buf = baos.toByteArray();
//打包 字节数组数据,指定目的主机端口
DatagramPacket dp = new DatagramPacket(buf, buf.length,new InetSocketAddress("127.0.0.1", 5678));
//创建套接字,指定UDP端口(本机)
DatagramSocket ds = new DatagramSocket(9999);
//发送数据包
ds.send(dp);
//释放套接字(DatagramSocket)
ds.close();
}
}
UDP Server 端
import java.net.*;
import java.io.*;
public class TestUDPServer
{
public static void main(String args[]) throws Exception
{
byte buf[] = new byte[1024];
//创建数据包,用来接收数据
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//创建套接字,指定监听端口
DatagramSocket ds = new DatagramSocket(5678);
while(true)
{
//接收数据,并放入buf[]中
ds.receive(dp);
//利用流转换,读取
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readLong());
}
}
}