Java网络编程

Java与Socket

Java对Socket底层支持进行了一套完成的封装,可以通过Java实现Socket通信。

要实现Socket通信,我们必须创建一个数据发送者和一个数据接收者,也就是客户端和服务端,我们需要提前启动服务端来等待客户端的连接,而客户端只需要随时启动去连接服务器端即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//服务端
package org.ep;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
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();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//客户端
package org.ep;

import java.io.IOException;
import java.net.Socket;

public class Client {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080)) {
System.out.println("已连接到服务端!");
} catch (IOException e) {
System.out.println("服务端连接失败!");
e.printStackTrace();
}
}
}

先运行服务器端:

1
正在等待客户端连接...

再运行客户端:

1
已连接到服务端!

此时的服务器端输出:

1
2
正在等待客户端连接...
客户端已连接,IP地址为: 127.0.0.1

实际上这个过程是一个TCP连接的建立过程,Java封装了这个底层实现的过程:
TCP
一旦TCP连接建立,服务端和客户端之间就可以相互发送数据,直到有一方主动关闭连接。

服务端不仅仅只可以让一个客户端进行连接,我们可以尝试让服务端一直运行来不断接受客户端的连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.ep;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
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();
}
}
}
1
2
3
4
正在等待客户端连接...
客户端已连接,IP地址为: 127.0.0.1
客户端已连接,IP地址为: 127.0.0.1
客户端已连接,IP地址为: 127.0.0.1

使用Socket进行数据传输

使用Socket对象,我们可以获取到对应的I/O流进行网络数据传输:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//客户端Client.java
package org.ep;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080);
Scanner sc = new Scanner(System.in)) {
System.out.println("已连接到服务端");
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
writer.write(sc.nextLine() + '\n'); //因为服务端使用reanline获取,所以需要有换行符
writer.flush();
System.out.println("数据已发送");
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//服务端Server.java
package org.ep;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
public static void main(String[] args) {
try (ServerSocket server = new ServerSocket(8080)) {
System.out.println("正在等待客户端连接......");
Socket socket = server.accept();
System.out.println("客户端已连接,IP地址为:" + socket.getInetAddress().getHostAddress());
System.out.println("读取客户端数据:");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(reader.readLine());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
  1. 先运行服务端,创建一个ServerSocket对象并监听8080端口,等待客户端连接。
  2. 运行客户端,创建一个Socket对象,用于连接到本地主机的8080端口。
  3. 客户端创建一个OutputStreamWriter对象,用于将文本数据写入socket的输出流socket.getOutputStream(),而这个OutoutStreamWriter对象得到的文本来源于Scanner的输入。
  4. 服务端通过InputStreamReader得到文本数据并打印,完成服务器端与客户端的通信。

既然服务端可以读取客户端的内容,客户端也可以在发送后等待服务器给予响应:

1

我们可以手动关闭单向的流:

1
2
socket.shutdownOutput();  //关闭输出方向的流
socket.shutdownInput(); //关闭输入方向的流

如果我们不希望服务端等待太长的时间,我们可以通过调用setSoTimeout()方法来设定IO超时时间:

1
socket.setSoTimeout(3000);