# 简单介绍
基于 TCP/IP协议 (opens new window) 的 Java
方式的Socket实现,它是 流套接字 (SOCK_STREAM) 的一种传输类型,是 面向连接 的服务类型。通过 Socket 数据流的方式进行通信和交互,特点是稳定性链接和长链接,适合重要数据、长时间持续通信、安全交互等等场景。
其实 socket 在我们日常使用的许多互联网软件中是很常见的,用于需要稳定和长时间的连接进行相关交互的场景,接下来开始我们的 JavaSocket TCP 的相关实战吧~
# 实现流程
- 服务端创建一个指定端口的
ServerSocket
; - 开启服务端Socket的监听,等待客户端的链接后接受并处理客户端请求;
- 创建一个客户端
SocketClient
,链接到指定IP和端口的ServerSocket
; - 客户端链接完成后,发送请求给服务端并接受服务端的响应产生交互行为。
- 在服务完成指定的任务后,需要关闭客户端和服务端之间的连接,服务端也可以根据需要是否需要继续监听连接请求,决定是否关闭服务程序。
# 创建服务端代码
public class SocketServer {
private static final String TAG = ServerSocket.class.getSimpleName();
private int port;
private boolean receive = true;
private ServerSocket serverSocket;
/**
* 创建一个指定port的SocketServer对象.
*
* @param port 绑定指定的port.
*/
public SocketServer(int port) {
this.port = port;
}
/**
* 创建SocketServer,并绑定指定的port,接收客户端的访问.
*
* @throws IOException
*/
public synchronized void run() throws IOException {
serverSocket = new ServerSocket(port);
receive = true;
while (receive) {
System.out.println(TAG + " run: Server receiving...");
Socket accept = serverSocket.accept();
byte[] buff = new byte[1024 * 8];
// 连接设备的address
InetAddress inetAddress = accept.getInetAddress();
// 连接设备的port
int port = accept.getPort();
System.out.println(TAG + " run: receive ==> client address = " + inetAddress + ", port = " + port);
// 获取设备的输入流
InputStream inputStream = accept.getInputStream();
// 接收数据的字符缓冲
StringBuilder sb = new StringBuilder();
while (true) {
int len = inputStream.read(buff);
if (len == -1) { // 断开链接
break;
}
// 将读取的字节流转换为字符对象,并append到StringBuilder中
sb.append(new String(buff, 0, len, StandardCharsets.UTF_8));
// 将接收到的消息打印出来
System.out.println(TAG + " run: receive msg = " + sb.toString().trim());
// 回复客户端消息
accept.getOutputStream().write(("Server msg has receive: msg = " + sb.toString()).getBytes(StandardCharsets.UTF_8));
// 清除StringBuilder中的数据
sb.delete(0, sb.length());
}
// 关闭相关流对象
inputStream.close();
accept.close();
}
}
/**
* 关闭SocketServer.
*
* @throws IOException
*/
public void shutDown() throws IOException {
System.out.println(TAG + " shutDown:");
if (null != serverSocket) {
receive = false;
serverSocket.close();
}
}
}
# 创建客户端代码
public class SocketClient {
private static final String TAG = SocketClient.class.getSimpleName();
private Socket socket;
private OutputStream outputStream;
private InputStream inputStream;
private volatile boolean isReceiving = false;
/**
* 创建一个SocketClient,并且连接到指定host和port的Server.
*
* @param host 目标Server的host.
* @param port 目标Server的port.
* @throws IOException
*/
public SocketClient(String host, int port) throws IOException {
socket = new Socket(host, port);
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
}
/**
* 向Server发送指定的msg数据.
*
* @param msg 发送的字符串数据.
* @throws IOException
*/
public synchronized void sendMessage(String msg) throws IOException {
if (null != msg && msg.length() > 0 && null != outputStream) {
outputStream.write(msg.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
System.out.println(TAG + " sendMessage: msg is send, msg = " + msg);
}
}
/**
* 断开Server的连接.
*
* @throws IOException
*/
public synchronized void disConnect() throws IOException {
isReceiving = false;
if (null != socket) {
if (null != outputStream) {
outputStream.close();
}
if (null != inputStream) {
inputStream.close();
}
inputStream = null;
outputStream = null;
socket.close();
socket = null;
System.out.println(TAG + " disConnect: ");
}
}
/**
* 接收Server的消息.
*/
public void receiveMessage() {
if (!isReceiving) {
isReceiving = true;
new Thread(new Runnable() {
@Override
public void run() {
if (null != inputStream) {
StringBuilder sb = new StringBuilder();
try {
while (true) {
byte[] buff = new byte[1024 * 8];
int readLen = inputStream.read(buff);
if (readLen == -1) { // 断开链接
break;
}
sb.append(new String(buff, 0, readLen, StandardCharsets.UTF_8));
System.out.println(TAG + " run: receiveMessage msg = " + sb.toString().trim());
sb.delete(0, sb.length()); // 一条消息读取结束,清除缓存
}
} catch (Exception e) {
isReceiving = false;
System.out.println(TAG + " receiveMessage finish");
}
}
}
}).start();
} else {
System.out.println(TAG + " receiveMessage: SocketClient has receiving...");
}
}
}
# 使用示例
/**
* Java Tcp Socket Demo.
*
* @throws IOException
*/
public static void javaTcpSocketDemo() throws Exception {
// 启动一个JavaServerServer,绑定一个端口号,并监听客户端的链接
SocketServer socketServer = new SocketServer(9999);
new Thread(new Runnable() {
@Override
public void run() {
try {
// 开启Server
socketServer.run();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(500); // 多线程延迟,防止异步创建对象不同步问题
// 本处ip是本地host,也可以是局域网下Server设备的实际ip
SocketClient client = new SocketClient("127.0.0.1", 9999);
client.receiveMessage(); // 客户端接收回复消息
// 接收输入的数据发送给Server
Scanner scanner = new Scanner(System.in);
while (true) {
Thread.sleep(500); // 数据输入发送间隔设置为500ms
System.out.println("\n==> [Client --> Server]请输入发送的消息(输入quit退出):");
String s = scanner.nextLine();
if ("quit".equals(s)) {
client.disConnect();
socketServer.shutDown();
break;
}
// 发送输入的字符串数据
client.sendMessage(s);
}
System.out.println("------------------------- javaTcpSocketDemo finished -------------------------");
}
# 运行示例
运行程序后,SocketClient
通过控制台的输入的数据向 ServerSocket
发送了数据,服务端收到消息后反馈给客户端一个已经接收消息的反馈消息,最后客户端执行断开链接,当前的链接结束。
ServerSocket run: Server receiving...
ServerSocket run: receive ==> client address = /127.0.0.1, port = 41710
==> [Client --> Server]请输入发送的消息(输入quit退出):
Hello JavaSocket, I am JiangMing.
SocketClient sendMessage: msg is send, msg = Hello JavaSocket, I am JiangMing.
ServerSocket run: receive msg = Hello JavaSocket, I am JiangMing.
SocketClient run: receiveMessage msg = Server msg has receive: msg = Hello JavaSocket, I am JiangMing.
==> [Client --> Server]请输入发送的消息(输入quit退出):
quit
SocketClient disConnect:
ServerSocket shutDown:
SocketClient receiveMessage finish
可以看到,程序已经成功的运行起来,Socket客户端(SocketClient)与服务端(ServerSocket)已经运行并产生了数据的交互了,好了简单的JavaSocket的TCP链接的使用介绍就到此结束了,有兴趣的朋友可以去参考相关资料,进行更深一步的研究~