连接
简单示例
1 | Socket s = new Socket("yuyurbq.club", 80); |
套接字超时
对于读写操作没有数据可供访问而阻塞:使用 SetSoTimeout
方法设置(毫秒),若之后的读写操作在时间内没有完成将抛出 SocketTimeoutException 异常。
对于一直无法建立到主机的连接而阻塞:先构建无连接的套接字,再通过有超时参数的方法进行连接:
1 | Socket s = new Socket(); |
获取 ip
静态方法 getByName
方法将返回代表某主机的 InetAddress 对象,通过 getAddress
获取封装其中的 ip 字节序列:
1 | InetAddress address = InetAddress.getByName(host); |
若主机名拥有多个地址,且希望获得所有这些:
1 | InetAddress[] addresses = InetAddress.getAllByName(host); |
还有个 getLocalHost
避免获得 127.0.0.1 这样的本地地址。
服务端
服务端套接字
建立套接字:
1 | ServerSocket s = new ServerSocker(port); // port 不是字符串 |
监听这个端口,一旦有客户端连接上(连接已建立)这个端口,将返回一个 Socket 对象,并以此获得输入输出流:
1 | Socket incoming = s.accept(); |
将流转换成扫描器和写入器:
1 | Scanner in = new Scanner(inStream, "UTF-8"); |
通讯的最后关闭套接字:
1 | incoming.close(); |
多个客户端
通过线程解决,类似:
1 | while (true) { |
ThreadedEchoHandler 实现 Runnable 接口,在 run 方法中包含与客户端通信的代码:
1 | class ThreadedEchoHandler implements Runnable { |
半关闭
socket 对象的 shutdownOutput()
方法可以终止输出但保持接收,适合 HTTP 这种 one-shot 服务。
可中断套接字
套接字读写数据时,当前线程将被阻塞至操作成功或超时为止,此时想取消连接的话,当线程因套接字无法响应而阻塞时,无法通过调用 interrupt 解除。
为了中断套接字操作:
1 | SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port)); |
WEB
URL 和 URI
1 | [scheme:]schemeSpecificPart[#fragment] |
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 类更多的控制功能。
URL 类的 openConnection 方法获得对象:
1
URLConnection connection = url.openConnection();
设置任意的请求属性,如
setDoInput
setDoOutput
setUseCaches
等;用 connect 方法连接:
1
connection.connect();
可以进行头信息的查询,
getHeaderFields
返回一个包含消息头所有字段的 Map 对象,通过getContentType
getDate
getContentLength
等查询字段;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 | URL url = new URL("http://host/path"); |
使用 setDoOutput 方法建立用于输出的连接:
1 | connection.setDoOutput(true); |
然后用 getOutputStream 方法获得一个流,并包装在 PrintWriter 对象中:
1 | PrintWrite out = new PrintWriter(connection.getOutputStream(),"UTF-8"); |
通过 out.print() 发送数据,最后 out.close() 关闭流。