在许多应用场景中,如车辆追踪、移动设备定位等,GPS定位数据的实时获取和处理至关重要。本文将介绍如何使用Java Socket编程来接收GPS设备发送的数据并进行处理。
业务说明:
车辆追踪系统需要实时获取车辆的GPS定位信息。车辆上的GPS设备通过TCP连接发送其位置数据到服务器。
技术点:
Java Socket编程:使用Java的Socket API进行网络通信。
GPS定位数据:理解GPS数据格式,如NMEA 0183标准。
多线程处理:为每个连接创建线程以异步接收数据。
项目结构:
gps-tracking-system/
|-- src/
| |-- main/
| | |-- java/
| | | `-- com/
| | | `-- example/
| | | |-- GpsServer.java
| | | |-- GpsDataHandler.java
| | | |-- GpsDataParser.java
| | | |-- BusinessDataService.java
| | | `-- RabbitMqProducer.java
| | `-- resources/
| | `-- application.properties
| `-- test/
| `-- java/
| `-- com/
| `-- example/
| `-- GpsServerTest.java
`-- pom.xml
GpsServer.java:服务器启动类。
GpsDataHandler.java:Netty通道处理器,用于处理接收到的GPS数据。
GpsDataParser.java:解析GPS数据的类。
BusinessDataService.java:业务数据处理服务,负责将解析后的数据发送到MQ。
RabbitMqProducer.java:RabbitMQ生产者实现。
application.properties:应用配置文件。
GpsServerTest.java:服务器的单元测试类。
Maven依赖:
在pom.xml文件中,您需要添加以下依赖:
<dependencies>
<!-- Netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
</dependency>
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- Logback Classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- RabbitMQ Java Client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
<!-- JUnit for testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.io.IOException;
import java.util.Properties;
public class GpsServer {
private final int port;
public GpsServer() throws IOException {
Properties props = new Properties();
props.load(getClass().getClassLoader().getResourceAsStream("application.properties"));
this.port = Integer.parseInt(props.getProperty("gps.server.port"));
}
public void run() throws InterruptedException {
MqProducer mqProducer = new RabbitMqProducer("mq-broker1:5672");
BusinessDataService businessDataService = new BusinessDataService(mqProducer);
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new GpsDataHandler(businessDataService));
}
});
b.bind(port).sync().channel().closeFuture().await();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new GpsServer().run();
}
}
GPS数据处理通道(GpsDataHandler.java):
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GpsDataHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(GpsDataHandler.class);
private final BusinessDataService businessDataService;
public GpsDataHandler(BusinessDataService businessDataService) {
this.businessDataService = businessDataService;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
try {
byte[] data = new byte[in.readableBytes()];
in.readBytes(data);
String gpsData = new String(data, StandardCharsets.UTF_8);
businessDataService.processBusinessData(gpsData);
} catch (Exception e) {
logger.error("Error processing GPS data", e);
} finally {
in.release();
ctx.close(); // Close the connection after processing
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Exception caught in GPS data handler", cause);
ctx.close();
}
}
业务数据服务(BusinessDataService.java):
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BusinessDataService {
private static final Logger logger = LoggerFactory.getLogger(BusinessDataService.class);
// MQ客户端或生产者,具体实现根据所使用的MQ进行调整
private final MqProducer mqProducer;
public BusinessDataService(MqProducer mqProducer) {
this.mqProducer = mqProducer;
}
public void processBusinessData(String gpsData) {
try {
BusinessGpsData businessData = GpsDataParser.parse(gpsData);
if (businessData != null) {
mqProducer.send(businessData);
logger.info("Business GPS data sent to message queue: {}", businessData);
}
} catch (Exception e) {
logger.error("Error processing business GPS data", e);
}
}
}
业务GPS数据解析器(GpsDataParser.java):
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GpsDataParser {
// 假设GPS数据格式为: $GPGGA,xxx,xxx,xxx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx*
private static final Pattern GPGGA_PATTERN = Pattern.compile("\\$GPGGA,\\d{2},\\d{2},(\\d{2}\\.\\d{7}),(\\d{3}),(\\d{2}\\.\\d{7}),([NS]),(\\d{3}\\.\\d{7}),([EW]),\\d{2}\\.\\d,\\d{2}\\.\\d,\\d{2},M,\\d{2},M,\\d{2},\\d{2}\\*[0-9A-F]{2}");
/**
* 例如,提取车辆ID、经纬度、时间戳等信息,并创建BusinessGpsData对象
* 解析NMEA 0183标准的GPGGA语句
* @param nmeaSentence GPS数据字符串
* @return 解析后的BusinessGpsData对象
*/
public static BusinessGpsData parse(String nmeaSentence) {
Matcher matcher = GPGGA_PATTERN.matcher(nmeaSentence);
if (matcher.find()) {
double latitude = Double.parseDouble(matcher.group(1));
String latHemisphere = matcher.group(3);
double longitude = Double.parseDouble(matcher.group(4));
String longHemisphere = matcher.group(6);
// 转换经纬度为统一格式
latitude = latHemisphere.equals("S") ? -latitude : latitude;
longitude = longHemisphere.equals("W") ? -longitude : longitude;
return new BusinessGpsData(
latitude,
longitude,
// 此处添加其他解析字段...
matcher.group(2) // 时间戳
);
}
return null;
}
}
// 定义业务GPS数据的字段,如车辆ID、经纬度、时间戳等
class BusinessGpsData {
private final double latitude;
private final double longitude;
// 其他业务字段...
public BusinessGpsData(double latitude, double longitude, String timestamp) {
this.latitude = latitude;
this.longitude = longitude;
// 初始化其他业务字段...
this.timestamp = timestamp;
}
// Getters and Setters
}
消息队列生产者接口(MqProducer.java):
public interface MqProducer {
void send(BusinessGpsData businessData);
}
消息队列生产者实现(RabbitMqProducer.java):
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitMqProducer implements MqProducer {
private final String queueName;
private final ConnectionFactory factory;
private Connection connection;
private Channel channel;
public RabbitMqProducer(String host) {
this.queueName = "gps_data_queue";
this.factory = new ConnectionFactory();
this.factory.setHost(host);
try {
this.connection = factory.newConnection();
this.channel = connection.createChannel();
this.channel.queueDeclare(queueName, true, false, false, null);
} catch (IOException | TimeoutException e) {
e.printStackTrace();
// Handle exception
}
}
@Override
public void send(BusinessGpsData businessData) {
String message = "BusinessGpsData [" +
"latitude=" + businessData.getLatitude() +
", longitude=" + businessData.getLongitude() +
// 其他字段...
", timestamp=" + businessData.getTimestamp() +
"]";
try {
channel.basicPublish("", queueName, null, message.getBytes());
} catch (IOException e) {
e.printStackTrace();
// Handle exception
}
}
// Ensure resources are cleaned up
public void close() {
if (channel != null) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
配置文件(resources/application.properties):
gps.server.port=6789
mq.broker.list=mq-broker1:5672