1. 连接到服务器
1.1 telnet
telnet 是用于网络编程的强大调试工具,可远程连接和服务通信。windows 的 telnet 需要单独激活
telnet time-a.nist.gov 13
获取标准时间
1.2 Socket
套接字(Socket)作用与 telnet 相同,java.net 包提供的接口与 io 包基本相同。
Socket s = new Socket("time-a.nist.gov", 13);
InputStream inStream = s.getInputStream();
1.3 套接字超时
套接字读操作阻塞,当主机不可达时,会因操作系统限制而超时。
主动设置超时,会抛异常,可处理
Socket s = new Socket(...);
s.setTimeout(10000);
普通构造器会无限阻塞,直至连接成功。可先建造一个无连接套接字,然后再使用超时连接。
Socket s = new Socket();
s.connect(new InetSocketAddress(host, port),timeout);
1.4 可中断套接字
套接字线程会阻塞至建立连接或超时,无法中断阻塞。为了能中断套接字操作,可以使用 nio 包新特性:SocketChannel 类。如果线程在执行打开、读取、写入操作时发生线程中断,操作不会阻塞,而会抛出异常。
1)打开通道
SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));
2)读取信息
Scanner in = new Scanner(channel, "UTF-8");
//OR
OutputStream outStream = Channerl.newOutputStram(channel);
1.5 因特网地址(InetAdress)
InetAdress 类可在主机名与因特网地址直接转换。
获取因特网地址:getByName、getAllByName、getLocalHost
//任意一个
InetAdress address = InetAdress.getByName("time-a.nist.gov");
//所有
InetAdress[] addresses = InetAdress.getAllByName("time-a.nist.gov");
//本机ip,不是127
InetAdress localhost = InetAdress.getLocalHost();
//获取字节
byte[] addressBytes = address.getAddress();
2 实现服务器
2.1 服务器套接字
ServerSocket 类用于建立套接字
ServerSocket s = new ServerSocket(8189);
不停等待,直到建立连接
Socket incoming = s.accept();
输入流来自客户端,发送给输出流的会变为客户端的输入
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
发送文本
Scanner in = new Scanner(inStream, "UTF-8");
PrintWriter out = new PrintWriter(new OutputStreamWrite(outStream), "UTF-8"), true/*autoFlush*/)
out.println("Hello!Enter BYE to exit.");
关闭连接
String line = in.nextLine();
out.println("Echo:" + line);
if(line.trim().equals("BYE")) done = true;
incoming.close();
每个服务器程序,都会不间断执行下面这个循环
- 通过输入流从客户端接收一个命令
- 解码客户端命令
- 收集客户端请求的信息
- 通过输出流发送信息给客户端
2.2 为多个客户端服务
运用线程可支持多连接,每当程序建立一个新的套接字连接时,启动一个新线程来处理链接,而主程序立即返回并等待下一个连接。
while(true){
Socket incoming = s.accept();
Runnable r = new ThreadedEchoHandler(incoming);
Thread t = new Tread(r);
t.start();
}
为使服务器实现更高的吞吐量,可以使用 nio 包的一些特性。
2.3 半关闭
半关闭是:套接字连接的一端可以终止其输出,同时仍旧接受另一端的数据。
可以通过关闭一个套接字的输出流来表示发送给服务器的请求数据已结束,但必须保持输入流处于打开状态
try(Socket socket = new Socket(host, port)){
Scanner in = new Scanner(socket.getInputStream(),"UTF-8");
PrintWriter writer = new PrintWriter(socket.getOutputStream());
//发送数据
write.print(...);
write.flush();
socket.shutdownOutput();
//半关闭读取
while(in.hasNextLine() != null){
String line = in.nextLine();
...
}
}