1.背景介绍
笔者在最近的项目中需要使用到TCP连接进行数据通信,但此前笔者对于TCP通信在项目中的应用一窍不通,更别提听说到要直接用TCP连接了,当时我真的是脑子嗡了一下。不过平复了一下情绪去做了以后,发现这件事情其实并没有想象中那么困难,甚至还挺有意思的,这样一个心理的过程我觉得在面对难题的时候尤其容易产生,我们太容易害怕了,如果勇敢点,我们做了一段时间以后,甚至会笑嘻嘻地说:“嘿!我竟然会为这么个小东西害怕,太好笑了!”
这里笔者啰嗦几句,权当是热身,并不影响后续的技术介绍,热身完毕,我们该进入正题了。
2.TCP简单介绍
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 [1] 定义。TCP通信一般指客户端和服务端通信。
在Java中,一般使用SocketServer和Socket这两个类实现TCP通信,前者为服务端的一个实体,而后者可以认为是一种对连接的封装。一般来说,需要先启动服务端,然后客户端才能向服务端发送连接请求,连接成功后,两端就可以互相通信了。
3.Java中TCP通信流程
- 服务端:
- 创建ServerSocket对象,绑定监听端口。
- 通过accept()方法监听客户端请求。
- 连接建立后,通过输入流读取客户端发送的请求信息。
- 通过输出流向客户端发送响应信息。
- 关闭响应的资源。
- 客户端:
- 创建Socket对象,指明需要连接的服务器的地址和端口号。
- 连接建立后,通过输出流向服务器发送请求信息。
- 通过输入流获取服务器响应的信息。
- 关闭相应资源。
4.示例代码
服务端:
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author: LJ
* @description:TCP服务端简单实现
**/
public class TCP_Server {
public static void main(String[] args)throws IOException
{
//监听端口
int port = 30000;
//创建一个ServerSocket, 用于监听客户端Socket的连接请求
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("server启动成功,等待客户端连接...");
//server等待连接的到来
Socket socket = serverSocket.accept();
//将Socket对应的输出流包装成printStream
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("You have received message form server!");
InputStream inputStream = socket.getInputStream();
byte[] bytesFromClient = new byte[1024];
int len = 0;
StringBuilder sb = new StringBuilder();
while ((len = inputStream.read(bytesFromClient))!=-1){
sb.append(new String(bytesFromClient, 0 , len));
}
System.out.println("get message from client: "+ sb.toString());
//关闭输出流,关闭socket
inputStream.close();
ps.close();
socket.close();
}
}
客户端:
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author: LJ
* @description:TCP客户端简单实现
**/
public class TCP_Client {
public static void main(String[] args) throws IOException {
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
OutputStreamWriter osw = null;
PrintWriter pw = null;
try{
//建立连接
Socket socket = new Socket("localhost", 30000);
System.out.println("开启Socket线程");
//获取字节输出流
os = socket.getOutputStream();
//将输出流包装为打印流
pw = new PrintWriter(os);
pw.write("Hi server! This is message from client!");
pw.flush();
//关闭输出流
socket.shutdownOutput();
//获取字节输入流
is=socket.getInputStream();
br=new BufferedReader(new InputStreamReader(is));
String message =null;
while ((message = br.readLine())!=null){
System.out.println("我是客户端,服务器返回消息:"+message);
}
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
br.close();
is.close();
pw.close();
os.close();
}
}
}
5.实验
编写好代码以后,我们可以开始测试代码了,首先启动服务器。(需要注意的是,两个程序需要同时运行,也就是说,你得启动两个集成开发环境保证客户端和服务端同时运行)控制台打印如下:
server启动成功,等待客户端连接...
接着启动客户端,这时服务端打印如下:
server启动成功,等待客户端连接...
get message from client: Hi server! This is message from client!
客户端打印如下:
开启Socket线程
我是客户端,服务器返回消息:You have received message form server!
实验结束,我们看到TCP通信成功了,也就是说我们编写的代码成功了!当然,这只是最基础的TCP通信,一般来说,你的服务端不可能只服务一个客户端,所以你需要为每一个连接创建一个线程,更复杂的情况需要使用一个线程池来保证运行效率。另外,TCP服务端如何检测TCP客户端断开连接也是一个令人头疼的问题,因为TCP连接是一个长连接,而默认情况下这个连接的持续时间会长达2个小时,如果断开连接而程序没有检测到,那么为了维持这个连接就浪费了很多的资源,这不是一件令人高兴的事情。
后续如果还有时间的话,最后提到的这两个问题,笔者也会给出自己实际采用的解决方案,不过那就不知道会是什么时候了,或许鸽了也说不定 😃 (靠,我的符号怎么变了,补一下— : ))。
参考资料:
Java–Socket编程TCP通信 - 知乎 (zhihu.com)
Java实现TCP通信 - 简书 (jianshu.com)