Java网络编程
学习资料:
要实现两台计算机互相发送数据,就必须要按照一定的规则进行数据的发送和接受,而Java早已为我们封装好了相应的API接口,只需要直接使用即可轻松实现网络通信
1、Socket
Socket 称之为 套接字
,是操作系统底层提供的一项通信技术,支持 TCP 和 UDP 。
在 Java 中,要实现 socket 通信,必须创建一个数据发送者(client)和一个数据接收者(server)。需要提前启动 server,来等待 client 的连接:
java
//服务端
public static void main(String[] args) {
try(ServerSocket server = new ServerSocket(8080)){ //将服务端创建在端口8080上
System.out.println("正在等待客户端连接...");
Socket socket = server.accept(); //当没有客户端连接时,线程会阻塞,直到有客户端连接为止
System.out.println("客户端已连接,IP地址为:"+socket.getInetAddress().getHostAddress());
}catch (IOException e){
e.printStackTrace();
}
}
java
//客户端
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080)){
System.out.println("已连接到服务端!");
}catch (IOException e){
System.out.println("服务端连接失败!");
e.printStackTrace();
}
}
实际上这是一个 TCP 连接的过程,一旦TCP连接建立,服务端和客户端之间就可以相互发送数据,直到客户端主动关闭连接。当然,服务端不仅仅只可以让一个客户端进行连接,可以尝试让服务端一直运行来不断接受客户端的连接:
java
public static void main(String[] args) {
try(ServerSocket server = new ServerSocket(8080)){ //将服务端创建在端口8080上
System.out.println("正在等待客户端连接...");
while (true){ //无限循环等待客户端连接
Socket socket = server.accept();
System.out.println("客户端已连接,IP地址为:"+socket.getInetAddress().getHostAddress());
}
}catch (IOException e){
e.printStackTrace();
}
}
2、socket数据传输
并且通过 socket 还可以获取到对应的 I/O 流进行网络数据传输:
java
// 客户端
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080);
Scanner scanner = new Scanner(System.in)){
System.out.println("已连接到服务端!");
OutputStream stream = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(stream); //通过转换流来帮助我们快速写入内容
System.out.println("请输入要发送给服务端的内容:");
String text = scanner.nextLine();
writer.write(text+'\n'); //因为对方是readLine()这里加个换行符
writer.flush();
System.out.println("数据已发送:"+text);
}catch (IOException e){
System.out.println("服务端连接失败!");
e.printStackTrace();
}finally {
System.out.println("客户端断开连接!");
}
}
}
java
// 服务端
public static void main(String[] args) {
try(ServerSocket server = new ServerSocket(8080)){ //将服务端创建在端口8080上
System.out.println("正在等待客户端连接...");
Socket socket = server.accept();
System.out.println("客户端已连接,IP地址为:"+socket.getInetAddress().getHostAddress());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //通过
System.out.print("接收到客户端数据:");
System.out.println(reader.readLine());
socket.close(); //和服务端TCP连接完成之后,记得关闭socket
}catch (IOException e){
e.printStackTrace();
}
}
同理,客户端也可以在发送后等待服务端给予响应:
java
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080);
Scanner scanner = new Scanner(System.in)){
System.out.println("已连接到服务端!");
OutputStream stream = socket.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(stream); //通过转换流来帮助我们快速写入内容
System.out.println("请输入要发送给服务端的内容:");
String text = scanner.nextLine();
writer.write(text+'\n'); //因为对方是readLine()这里加个换行符
writer.flush();
System.out.println("数据已发送:"+text);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("收到服务器返回:"+reader.readLine());
}catch (IOException e){
System.out.println("服务端连接失败!");
e.printStackTrace();
}finally {
System.out.println("客户端断开连接!");
}
}
java
public static void main(String[] args) {
try(ServerSocket server = new ServerSocket(8080)){ //将服务端创建在端口8080上
System.out.println("正在等待客户端连接...");
Socket socket = server.accept();
System.out.println("客户端已连接,IP地址为:"+socket.getInetAddress().getHostAddress());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //通过
System.out.print("接收到客户端数据:");
System.out.println(reader.readLine());
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("已收到!");
writer.flush();
}catch (IOException e){
e.printStackTrace();
}
}
还可以手动关闭单向的流:
java
socket.shutdownOutput(); //关闭输出方向的流
socket.shutdownInput(); //关闭输入方向的流
以通过调用 setSoTimeout()
方法来设定IO超时时间:
java
socket.setSoTimeout(3000);
当超过设定时间都依然没有收到客户端或是服务端的数据时,会抛出异常:
java
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.test.Main.main(Main.java:41)
如果连接的双方发生意外而通知不到对方,导致一方还持有连接,这样会占用资源,可以使用 setKeepAlive()
方法来防止此类情况发生:
java
socket.setKeepAlive(true);
当客户端连接后,如果设置了 keeplive
为 true,当对方没有发送任何数据过来,超过一个时间(看系统内核参数配置),那么会发送一个ack探测包发到对方,探测双方的TCP/IP连接是否有效。