0%

Java 补丁-网络

连接

简单示例

1
2
3
Socket s = new Socket("yuyurbq.club", 80);
// 打开一个套接字
Scanner in = new Scanner(s.getInputStream(), "UTF-8");

套接字超时

对于读写操作没有数据可供访问而阻塞:使用 SetSoTimeout 方法设置(毫秒),若之后的读写操作在时间内没有完成将抛出 SocketTimeoutException 异常。

对于一直无法建立到主机的连接而阻塞:先构建无连接的套接字,再通过有超时参数的方法进行连接:

1
2
Socket s = new Socket();
s.connect(new InetSocketAddress(host, port), timeout);

获取 ip

静态方法 getByName 方法将返回代表某主机的 InetAddress 对象,通过 getAddress 获取封装其中的 ip 字节序列:

1
2
InetAddress address = InetAddress.getByName(host);
byte[] addressBytes = address.getAddress();

若主机名拥有多个地址,且希望获得所有这些:

1
InetAddress[] addresses = InetAddress.getAllByName(host);

还有个 getLocalHost 避免获得 127.0.0.1 这样的本地地址。

服务端

服务端套接字

建立套接字:

1
ServerSocket s = new ServerSocker(port); // port 不是字符串

监听这个端口,一旦有客户端连接上(连接已建立)这个端口,将返回一个 Socket 对象,并以此获得输入输出流:

1
2
3
Socket incoming = s.accept();
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();

将流转换成扫描器和写入器:

1
2
Scanner in = new Scanner(inStream, "UTF-8");
PrintWrite out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"), true);

通讯的最后关闭套接字:

1
incoming.close();

多个客户端

通过线程解决,类似:

1
2
3
4
5
6
while (true) {
Socket incoming = s.accept();
Runnable r = new ThreadedEchoHandler(incoming);
Thread t = new Thread(r);
t.start;
}

ThreadedEchoHandler 实现 Runnable 接口,在 run 方法中包含与客户端通信的代码:

1
2
3
4
5
6
7
8
class ThreadedEchoHandler implements Runnable {
public void run() {
try (InputStream inStream = incoming.get...) {
...
}
catch(IOException e) {...}
}
}

半关闭

socket 对象的 shutdownOutput() 方法可以终止输出但保持接收,适合 HTTP 这种 one-shot 服务。

可中断套接字

套接字读写数据时,当前线程将被阻塞至操作成功或超时为止,此时想取消连接的话,当线程因套接字无法响应而阻塞时,无法通过调用 interrupt 解除。

为了中断套接字操作:

1
2
SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));
// 服务器端 host 用 localhost 即可

WEB

URL 和 URI

1
2
3
[scheme:]schemeSpecificPart[#fragment]
分层 URI : schemeSpecificPart > [//authority][path][?query]
基于服务器的 URL : authority > [user-info@]host[:port]

URI

  • 语法结构,指定 Web 资源的字符串;
  • URI 类解析标识符并将其分解成组成部分,如 getScheme getHost getPath 等;
  • 还可以处理绝对和相对标识符,组合出绝对 URI 或相对化 URI,relativize(combined) resolve(relative)

URL

  • URI 的一个特例,定位 Web 资源;
  • 从字符串构建 URL 对象:URL url = new URL(urlString) ;
  • 从 URL 类的 openStream 方法获得 InputStream 对象,随后正常处理。

URN

  • 无法定位数据的 URI,统一资源名称。

从 URLConnection 获取信息

能获得比基本 URL 类更多的控制功能。

  1. URL 类的 openConnection 方法获得对象:

    1
    URLConnection connection = url.openConnection();
  2. 设置任意的请求属性,如 setDoInput setDoOutput setUseCaches 等;

  3. 用 connect 方法连接:

    1
    connection.connect();
  4. 可以进行头信息的查询,getHeaderFields 返回一个包含消息头所有字段的 Map 对象,通过 getContentType getDate getContentLength 等查询字段;

  5. getInputStream 方法访问资源数据,和 openStream 返回的流相同。

设置连接属性

默认建立的连接只产生读取信息的输入流,若要获得执行写操作的输出流(提交数据)使用:

1
connection.setDoOutput(true);

设置请求头:setIfModifiedSince 方法针对某特定日期以来的修改;setUseCaches 只作用于 Applet ;setUseCaches 方法命令浏览器先检查缓存;setAllowUserInteraction 用于访问密码保护资源时弹出对话框,等。

查询响应头

1
String key = connection.getHeaderfieldKey(n);

获得响应头的第 n 个键,n 从 1 开始,若 n 为 0 或大于消息头的字段总数则返回 null。此方法无法返回字段数量,只能遍历到 null 为止。类似的,getHeaderField(n) 获得第 n 个值。

getHeaderFields 方法返回封装了响应头字段的 Map 对象。还有获取常用消息头字段的方法:

方法 返回类型
Date getDate long
Expires getExpiration long
LastModified getLastModified long
Content-Length getContentLength int
Content-Type getContentType String
Content-Encoding getContent-Encoding String

提交表单数据

首先创建 URLConnection 对象:

1
2
URL url = new URL("http://host/path");
URLConnection connection = url.openConnection();

使用 setDoOutput 方法建立用于输出的连接:

1
connection.setDoOutput(true);

然后用 getOutputStream 方法获得一个流,并包装在 PrintWriter 对象中:

1
PrintWrite out = new PrintWriter(connection.getOutputStream(),"UTF-8");

通过 out.print() 发送数据,最后 out.close() 关闭流。