Java(1.8)高级特性-网络

网络编程涉及到了最基础的Socket编程,以及基于次的网络服务。下面将介绍在Java中如何实现Socket以及一些简单的网络客户端和服务端。

Socket

  1. 连接
    Socket类是Java中实现的基本的网络编程类,可以通过地址和端口来进行初始化,连接到该服务器的端口上,也可以使用无参构造函数,再调用connect()方法连接上服务器。两者的区别是当直接使用带参构造函数去初始化Socket时,进程会一直阻塞直到连接上服务器,而如果是在使用无参构造函数创建Socket后调用connect()方法,可以设置连接的时间限制,当超过时间后就会抛出IOException
Socket s1=new  Socket(String host,int port);
Socket s2=new Socket();
s2.connect(host,port,1000);//时限为1秒钟
  1. 通信
    Socket对象连接上服务器后,就可以通过socket.getInputStream()socket.getOutputStream()与服务器进行数据传输。这两个方法返回的是(In|Out)putStream对象,可以使用输入输出流的处理方法来进行读写。最后通过socket.close()来关闭。
  2. 其余方法
    此外,Socket类还实现了一些有助于平常使用的方法。
public InetAddress getInetAddress();//获取socket连接的服务器地址
public int getPort();//获取socket连接的服务器端口
public InetAddress getLocalAddress();//获取socket连接的本地的地址
public int getLocalPort();
public boolean isClosed();
...

ServerSocket

  1. 创建
    服务器程序会在某个端口等待用户程序的连接,在创建ServerSocket的时候,可以进行端口指定。
    创建完ServerSocket对象后,通过调用s.accept()方法来等待用户程序连接上服务器。该方法阻塞进程直到用户连接,并返回一个Socket对象。获取到Socket对象后就可以通过输入输出流向用户程序进行读写。
ServerSocket ss=new ServerSocket(port);
Socket s=ss.accept();
InputStream in=s.getInputStream();
OutputStream out=s.getOutputStream();
  1. 并发处理
    当要同时处理多个用户程序连接服务器的时候,可以在获得Socket对象后创建线程来处理该Socket的读写。
while(ture){
  Socket s=ss.accept();
  Thread t=new Thread(
    public void run(){
      //input and output
    }
  );
  t.start();
}
  1. 半关闭
    Socket对象提供了只关闭输入/输出流其中之一而不断开连接的方法,即shutdown(In|Out)put(),在断开后,任何输入或输出都将会被抛弃。断开后没有办法再重新连接上,因此这种方法只适用于一站式的服务。

因特网地址

Java中提供了InetAddressInetSocketAddress类来表示因特网地址。其中InetSocketAddress类单纯地用来表示一个地址,即ip:portInetAddress用来表示一个ip,并提供了用于获得域名对应的ip等功能。

public boolean isReachable(int timeout) throws IOException;
public String getHostName();
public static InetAddress getByName(String host);
//获取一个域名所有的ip地址
public static InetAddress[] getAllByName(String host);
//获取本地的ipv4地址
public static InetAddress getLocalHost() throws UnknownHostException;
... 

获取Web服务

使用Socket只能与指定的ip:port进行通信,而无法获得服务器特定目录下的资源,即无法通过URL获得资源(?)。URI是统一资源标识符,其中能够定位到数据的称为URL(统一资源定位符),不能定位到数据的称为URN(统一资源名称)。
Java提供了URL类对URL进行访问,提供了URI类对标识符进行解析。

  1. URI
    一个URI具有这样的格式,[scheme:]schemeSpecificPart[#fragment],其中[...]表示可选的部分,并且:#可以被包含在标识符内。包含[scheme:]部分的为绝对URI,否则为相对URI,其中schemeSpecificPart/开头的绝对URI为不透明的,例如mailto:[email protected]。所有透明的绝对URI和所有相对URI都是分层的,如https://test.com/index../../java/net/Socket.html#Socket()。一个分层的URI的schemeSpecificPart具有这样的格式,[//authority][path][?query],基于服务器的URI的authority部分具有这样的格式[user-info@]host[:port]
    URI类提供了解析标识符和处理相对(绝对)标识符的功能。其中resolve()方法提供了解析相对URI,将其生成绝对URI的方法,relativize()方法提供了相对化方法,将绝对URI生成相对URI。比如,b/chttps://a.com/b为参数调用resolve()将得到https://a.com/b/chttps://a.com/b/chttps://a.com/b为参数调用relativize()将得到c
public String getSchemeSpecificPart();
public String getAuthority();
public String getUserInfo();
public String getHost();
public int getPort();
...
  1. URL
    URL类提供了从给定url获得资源的方法。URL提供了与URI类似的解析标识符的方法,此外还可以通过调用openStream()获得InputStream对象来获取资源内容。不过URL类没有提供输出的方法,想要和服务器进一步交互,可以调用openConnection()方法来获得URLConnection对象来进一步操作。
URL url = new URL(urlString);
InputStream in = url.openStream();
  1. URLConnection
    该类提供了向服务器输出和接收服务器输入的功能。操作URLConnection需要遵循一下几个步骤:
    ⑴ 调用URLopenConnection()来获得 URLConnection对象。
    ⑵ 设置参数和请求的属性。
    ⑶ 调用connect()连接远程资源。
    ⑷ 建立连接后就可以查询头信息和访问资源数据。
  • 参数和请求头属性
    该类提供了一下方法设置参数和请求头属性(比如http header),setter都有对应的getter。默认情况下,建立的连接只允许从服务器接收输入流,如果想要向服务器发送数据,需要先设置setDoOutput(true)。修改请求头属性使用了键值对来进行修改,如果一个键可以有多个对应的值,则将值用list包装即可。
public void setAllowUserInteraction();
public void setDoInput();
public void setDoOutput();
public void setIfModifiedSince();//连接只对某个特定日期以来修改过的数据感兴趣
public void setUseCaches();
//修改请求头属性
public void addRequestProperty(String key, String value);
  • 建立连接后的查询访问
    建立连接后,该类还提供了如下方法进行头部的信息查询。
public Object getContent();
public String getHeaderField();
public InputStream getInputStream();
public  OutputStream getOutputStream();
public String getContentEncoding();
public int getContentLength();
public String getContentType();
public long getDate();
public long getExpiration();
public long getLastModifed();
  1. 提交表单
    想服务器提交表单有Get和Post两种方法。
  • Get
    对于Get方法来说,只需要遵循下面的规则将参数附在URL结尾处。
    ⑴ 保留字符A-Z、a-z、0-9、.、-、~、_。
    ⑵ 用+字符替换所有空格。
    ⑶ 将其他所有字符编码为UTF-8,并将每个字节都编码为%后面紧跟一个两位的十六进制数字。
    例如San Francisco,CA,可以使用San+Francisco%2+CA
  • Post
    Post方法将参数写入到输出流中,不在URL上显示长串的参数键值对。
URL url=new URL(urlString);
URLConnection connection=url.openConnection();
connection.setDoOutput(true);
PrintWriter out=new PrintWriter(connection.getOutputStream(),"utf-8");
out.print(name1+"="+URLEncoder.encode(value1,"utf-8")+"&");
out.print(name2+"="+URLEncoder.encode(value2,"utf-8"));
out.close();
  • 重定向
    服务器程序相应可能会产生重定向,重定向一般都是自动处理的,但是有些情况下,比如http与https之间的重定向因为安全原因不被支持,所以需要自己手动进行重定向。下面是手动处理重定向的步骤。
//在连接前关闭自动重定向
connection.setInstanceFollowRedirects(false);
//在发送请求之后获取相应码
int responseCode=connection.getResponseCode();
//如果是HttpURLConnection.HTTP_MOVED_PERM
//或HttpURLConnection.HTTP_MOVED_TEMP
//或HttpURLConnection.HTTP_SEE_OTHER
//就获取Location响应头,获得重定向URL,断开连接,创建到新的URL连接
String location=connection.getHeaderField("Location");
if(location!=null){
  URL base=connection.getURL();
  connection.disconnect();
  connection=(HttpURLConnection) new URL(base,location).openConnection();
}
HTTP

HTTP有0.9、1.0、1.1、2.0这几个版本。

  1. HTTP 0.9
    HTTP 0.9是第一个HTTP协议的版本,只支持GET请求,不支持请求头且没有协议头,只支持纯文本内容。该版本的协议就已经支持了无状态特性,事务结束就关闭请求,访问地址不存在也不会返回错误信息。
  2. HTTP 1.0
    HTTP 1.0在0.9的版本上支持了请求和响应头部、响应以一个响应状态行开始、响应信息不仅限于超文本、支持了POST请求。该版本默认使用了短连接,每次请求建立一个TCP连接。
  3. HTTP 1.1
    新增了keep alive、chunked编码传输、字节范围请求、请求流水线等特性。还支持了HOST域(即一个IP下可能有多个服务器,通过主机名指定)、增加了connect/trace/delete/put/options的请求方法。
  • keep alive
    该版本的HTTP协议默认使用长连接,即事务完成后不断开连接,除非客户端或服务器主动断开。
  • chunked编码传输
    该特性使得传输对象可以分块传输并标明长度,当长度表明为0时说明传输结束。
  • 字节范围请求
    当客户端已有请求对象的一部分内容时,可以向服务器请求其余部分的内容,而不用重新传输整个对象。
  1. HTTP 2.0
    该协议是下一代HTTP协议,以下是其特性。
  • 多路复用,将HTTP 1.1的包分为两帧,头部放在header帧,数据放在data帧,以实现加大带宽,降低延迟的目的。
  • 所有通信在一个连接上完成,该连接可以承载任意数量的双向传输,每个消息被分成多帧传输,到达后根据头部的信息进行重新组装。
  • 头部压缩,当一个客户端向同一服务器请求时,数据包的头部是相似的,HTTP 2.0提供了压缩的方法。
  • 随时复位,HTTP 2.0可以随时停止一个TCP连接的传输,在不中断的情况下传输新的内容。
  • 服务器端推流,客户端向服务器请求一个资源,服务器判断该客户端可能用到的其他资源,一并发送给客户端,客户端进行缓存。
  • 优先权和依赖,可以指明哪些资源优先级更高,可以更快得到处理。
HTTP包结构

一个HTTP包由一下部分构成:起始行、一个或多个头域、一个空行表示头域的结束、实体消息。

GET http://download.microtool.de:80/somedata.exe
Host: download.microtool.de
Accept:*/*
Pragma: no-cache
Cache-Control: no-cache
Referer: http://download.microtool.de/
User-Agent:Mozilla/4.04[en](Win95;I;Nav)
Range:bytes=554554-

HTTP包的第一行为起始行,请求包的起始行包含请求方法、URL、HTTP版本;响应包的起始行包含HTTP-Version Status-Code Reason-PhraseHTTP-Version表示支持的HTTP版本、Status-Code表示三位数字的结果代码、Reason-Phrase对结果代码的文本描述等。
上述请求包中HostAcceptPragma就为头域,通过:的形式表示。
头域可以分为通用头域、请求头域、响应头域和实体头域。

  • 通用头域:Date表示消息发送的时间、Pragma实现特定的指令比如Pragma:no-cacheConnection表示连接状态等。
  • 请求头域:Host表示请求资源的URL、Accept表示自己接收什么文件、Accept-Charset表示接收文件的编码格式、Authorization用来将身份验证信息发给服务器等。
  • 响应头域:AgeLocationProxy-AuthenticatePublic等。
  • 实体头域:请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成,Content-Type实体的介质类型、Content-Length表示实际传送的实体长度等。

你可能感兴趣的:(Java(1.8)高级特性-网络)