什么是TCP协议
TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复 用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、 流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。 TCP支持的应用协议主要有:Telnet、FTP、SMTP等; UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。 TCP/IP协议与低层的数据链路层和物理层无关,这也是TCP/IP的重要特点
Java如何对接
这里采用MINA框架进行对接TCP协议
- 引用依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.0.10</version>
</dependency>
- TCP服务端搭建
@Slf4j
@Component
public class TcpServer implements ApplicationRunner, ApplicationListener<ContextClosedEvent>, ApplicationContextAware {
// TCP端口
private int tcpPort;
private ApplicationContext applicationContext;
public static IoAcceptor accept;
@Override
public void run(ApplicationArguments args) throws Exception {
//服务端的实例
log.info("启动tcp服务...................");
accept=new NioSocketAcceptor();
// 采用框架的 编解码类 需要在发送数据后加 换行 \n
TextLineCodecFactory textLineCodec=new TextLineCodecFactory(Charset.forName("UTF-8"));
textLineCodec.setDecoderMaxLineLength(10*1024*1024);
textLineCodec.setEncoderMaxLineLength(10*1024*1024);
//添加filter,codec为序列化方式。这里为对象序列化方式,即表示传递的是对象。
accept.getFilterChain().addLast("codec",
new ProtocolCodecFilter(textLineCodec));
accept.setHandler((IoHandler)applicationContext.getBean(TcpMessageHandler.class));
accept.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
//绑定ip
accept.bind(new InetSocketAddress(tcpPort));
log.info("tcp服务启动成功...................");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
try {
log.info("关闭tcp服务");
accept.dispose();
} catch (Exception e) {
log.error("关闭tcp服务异常",e);
}
}
}
- TCP服务接收数据处理
@Slf4j
@Component
public class TcpMessageHandler extends IoHandlerAdapter {
@Override
public void sessionCreated(IoSession session){
SocketSessionConfig cfg = (SocketSessionConfig) session.getConfig();
cfg.setReceiveBufferSize(2 * 1024 * 1024);
cfg.setReadBufferSize(2 * 1024 * 1024);
cfg.setKeepAlive(true);
cfg.setReuseAddress(true);
cfg.setSoLinger(0); //这个是根本解决问题的设置,进程关闭,端口不残留
}
@Override
public void sessionOpened(IoSession session) {
InetSocketAddress remoteAddress = (InetSocketAddress)session.getRemoteAddress();
String clientIp = remoteAddress.getAddress().getHostAddress();
log.info("链接创建:{},{}", session.getId(),clientIp);
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) {
log.error("tcp服务异常", cause);
session.close(true);
}
@Override
public void messageReceived(IoSession session, Object message) throws UnsupportedEncodingException {
// log.info("获取message{}",message); 根据报文信息进行处理相关逻辑
String msg = message.toString();
}
@Override
public void sessionClosed(IoSession session) {
log.info("链接断开:{}", session.getId());
}
}
- TCP服务发送数据工具类
@Slf4j
public class TcpClient {
public static SoftServerResponse send(String ip, Integer port, String req) throws IOException {
String msg = req;
log.info("发送数据:ip:"+ip+",port:"+port+",req:"+msg);
Socket client = new Socket();
SocketAddress address=new InetSocketAddress(ip, port);
client.connect(address,5000);
client.setSoTimeout(20000);
//获取Socket的输出流,用来发送数据到服务端
PrintStream out = new PrintStream(client.getOutputStream(),false,"UTF-8");
//获取Socket的输入流,用来接收从服务端发送过来的数据
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));
//发送数据到服务端
out.print(msg);
out.flush();
//从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常
String result = buf.readLine();
while(result==null){
result = buf.readLine();
}
if(client!=null){
client.close();
}
log.info("服务器端返回的数据:" + result);
result = result.substring(result.indexOf("{"));
SoftServerResponse res = JacksonUtils.jsonToObject(result, SoftServerResponse.class);
return res;
}
}
TCP粘包问题如何解决
- 采用MINA默认的编辑码工具在交互时数据发送末尾添加换行(\n)
- 采用自定义数据头方式通过自定义数据头的字节长度进行数据解码和转换(需要自定义MINA的编解码类)
畅联AIoT开放云平台(www.24hlink.cn)由杭州美畅物联技术有限公司精心打造,不仅能够接入视频,也可以接入、管理各种IoT设备、工业现场设备,在AIoT开发领域能给合作伙伴带来难以置信的的降本增效价值。