因业务需要,需要开发一个引擎(采用JAVA),来解析SMPP包然后经过一系列处理入REDIS队列。看到解包,想到之前用C/C++进行SOCKET编程时,事先约定好,头4个字节作为一个整形字段,代表整个字节组的长度,后面依次其他。解析时候先解析出头部得到字节总长度,和获取到的字节数组比对看是否相等,然后依次解析后面字段。我想解析SMPP包也类似,所以就网上搜索一下SMPP的资料,摘抄如下:
消息头语法
字段
|
长度(字节)
|
类型
|
Command Length
|
4
|
Integer
|
Command ID
|
4
|
Integer
|
Command_status
|
4
|
Integer
|
Sequence No.
|
4
|
Integer
|
Optional Message Body
|
可变
|
混合
|
具体字段描述说明:
Command Length:整个包的长度(包括该字段本身)。。。看到这基本上明白,也是依次解析字段的。现在模拟一下,发包和解包的过程。
1 首先创建一个线程池来监听GlobalProperties.mClientPort端口。
private final ExecutorService execService = Executors.newSingleThreadExecutor();
SMPPServerSimulator smppServerSim = new SMPPServerSimulator(GlobalProperties.mClientPort);
execService.execute(smppServerSim);
1.1 SMPPServerSimulator 运行时会创建ServerSocket来监听GlobalProperties.mClientPort端口
public void run() {
try {
SMPPServerSessionListener sessionListener = new SMPPServerSessionListener(port);
logger.info("Listening on port {}", port);
while (true) {
SMPPServerSession serverSession = sessionListener.accept(); //阻塞至到有客户端连接进来
logger.info("Accepting connection for session {}", serverSession.getSessionId());
serverSession.setMessageReceiverListener(this);
serverSession.setResponseDeliveryListener(this);
execService.execute(new WaitBindTask(serverSession));
}
} catch (IOException e) {
logger.error("IO error occurred", e);
}
}
3 Client创建一个线程池来准备连接服务端
private final ExecutorService execService = Executors.newSingleThreadExecutor();
execService.execute(StressClient.getClient());
3.1 StressClient运行时会创建Socket来连接服务端
public void run() {
try {
smppSession.connectAndBind(host, port, BindType.BIND_TRX, systemId,
password, "cln", TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN, null);
logger.info("Bound to {}:{}", host, port);
} catch (IOException e) {
logger.error("Failed initialize connection or bind", e);
return;
}
new TrafficWatcherThread().start();
logger.info("Starting to send {} bulk messages", bulkSize);
for (int i = 0; i < bulkSize && !exit.get(); i++) {
execService.execute(newSendTask("Hello " + id + " idx=" + i));
}
while (!exit.get()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
logger.info("Done");
smppSession.unbindAndClose();
}
通过抓包可以看到在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。
4 双方建立起了通信连接,可以正常发送信息了。
4.1 查看SMPP协议,bind_transmitter命令代表,短消息实体(ESME/SME)作为客户端与短消息中心(SMSC)建立连接,本连接建立起来后,允许短消息实体向短消息中心提交短消息。也就是说在扩展短消息实体和短消息中心之间建立虚连接,接收SMSC转发的短消息。那么首先按照bind_transmitter命令提供参数,发送SMPP包。
字段
|
长度(字节)
|
类型
|
System_id
|
最大 16
|
C_String
|
Password
|
最大 9
|
C_String
|
System_type
|
最大 13
|
C_String
|
Interface_version
|
1
|
Integer
|
Addr_ton
|
1
|
Integer
|
Addr_npi
|
1
|
Integer
|
Address_range
|
最大 41
|
C_DecString
|
5 回到 上面 3.1 StressClient运行时会创建Socket来连接服务端,在run里面有个方法connectAndBind,看名字就明白了,连接和绑定(这里Bind指是发送bind_transmitter命令)
5.1 public String connectAndBind(String host, int port,
BindParameter bindParam, long timeout)
throws IOException {
logger.debug("Connect and bind to {} port {}", host, port);
if (sequence().currentValue() != 1) {
throw new IOException("Failed connecting");
}
conn = connFactory.createConnection(host, port);
logger.info("Connected to {}", conn.getInetAddress());
conn.setSoTimeout(getEnquireLinkTimer());
sessionContext.open();
try {
in = new DataInputStream(conn.getInputStream());
out = conn.getOutputStream();
pduReaderWorker = new PDUReaderWorker();
pduReaderWorker.start();
String smscSystemId = sendBind(bindParam.getBindType(), bindParam.getSystemId(), bindParam.getPassword(), bindParam.getSystemType(),
bindParam.getInterfaceVersion(), bindParam.getAddrTon(), bindParam.getAddrNpi(), bindParam.getAddressRange(), timeout);
sessionContext.bound(bindParam.getBindType()); //参数和上面发送bind_transmitter命令提供参数类似。
enquireLinkSender = new EnquireLinkSender();
enquireLinkSender.start();
return smscSystemId;
} catch (PDUException e) {
logger.error("Failed sending bind command", e);
throw new IOException("Failed sending bind since some string parameter area invalid: " + e.getMessage(), e);
} catch (NegativeResponseException e) {
String message = "Receive negative bind response";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (InvalidResponseException e) {
String message = "Receive invalid response of bind";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (ResponseTimeoutException e) {
String message = "Waiting bind response take time too long";
logger.error(message, e);
close();
throw new IOException(message + ": " + e.getMessage(), e);
} catch (IOException e) {
logger.error("IO error occurred", e);
close();
throw e;
}
}
5.2 通过抓包软件也可以看到发送了一个bind_transmitter命令的SMPP包,里面内容是3.1connectAndBind时传递过来的,如System type = "cln"
6 回到上面的1.1 里面的run方法,用另一个线程池来执行WaitBindTask(实现了Runnable的类),查看WaitBindTask的RUN方法
6.1 public void run() {
try {
BindRequest bindRequest = serverSession.waitForBind(1000); //阻塞,至到超时时间到
logger.info("Accepting bind for session {}, interface version {}", serverSession.getSessionId(), bindRequest.getInterfaceVersion());
try {
bindRequest.accept("sys", InterfaceVersion.IF_34);
} catch (PDUStringException e) {
logger.error("Invalid system id", e);
bindRequest.reject(SMPPConstant.STAT_ESME_RSYSERR);
}
} catch (IllegalStateException e) {
logger.error("System error", e);
} catch (TimeoutException e) {
logger.warn("Wait for bind has reach timeout", e);
} catch (IOException e) {
logger.error("Failed accepting bind request for session {}", serverSession.getSessionId());
}
}
6.2 跳转到waitForBind方法
public BindRequest waitForBind(long timeout) throws IllegalStateException,
TimeoutException {
SessionState currentSessionState = getSessionState();
if (currentSessionState.equals(SessionState.OPEN)) {
new PDUReaderWorker().start();
try {
return bindRequestReceiver.waitForRequest(timeout);
} catch (IllegalStateException e) {
throw new IllegalStateException("Invocation of waitForBind() has been made", e);
} catch (TimeoutException e) {
close();
throw e;
}
} else {
throw new IllegalStateException(
"waitForBind() should be invoked on OPEN state, actual state is "
+ currentSessionState);
}
}
查看PDUReaderWorker线程
@Override
public void run() {
logger.info("Starting PDUReaderWorker with processor degree:{} ...", getPduProcessorDegree());
while (isReadPdu()) {
readPDU();//读取SMPP包
}
close();
executorService.shutdown();
logger.info("PDUReaderWorker stop");
}
读到完信息之后,服务端要返回一个bind_transmitter_resp SMPP包给客户端,回到6.1run方法里面调用了bindRequest.accept,查看这个方法
public void accept(String systemId, InterfaceVersion interfaceVersion) throws PDUStringException, IllegalStateException, IOException {
StringValidator.validateString(systemId, StringParameter.SYSTEM_ID);
lock.lock();
try {
if (!done) {
done = true;
try {
responseHandler.sendBindResp(systemId, interfaceVersion, bindType, originalSequenceNumber);//返回一个bind_transmitter_resp SMPP包给客户端
} finally {
condition.signal();
}
} else {
throw new IllegalStateException("Response already initiated");
}
} finally {
lock.unlock();
}
done = true;
}
通过抓包软件可以看到服务端返回给客户端的bind_transmitter_resp SMPP包,如System type = "sys"
打印的LOG如下 服务端
2016-08-16 11:25:46,805 [pool-1-thread-1] INFO com.rongwu.SMPPServerSimulator - Listening on port 8056
2016-08-16 11:25:52,200 [pool-1-thread-1] INFO com.rongwu.SMPPServerSimulator - Accepting connection for session 3d92afa2
2016-08-16 11:25:52,202 [Thread-2] INFO com.rongwu.session.SMPPServerSession - Starting PDUReaderWorker with processor degree:3 ...
2016-08-16 11:25:52,203 [pool-4-thread-1] DEBUG com.rongwu.session.PDUProcessServerTask - Received SMPP message PDUHeader(31, 00000009, 00000000, 1) 00 00 00 1f 00 00 00 09 00 00 00 00 00 00 00 01 6a 00 6a 70 77 64 00 63 6c 6e 00 34 00 00 00
2016-08-16 11:25:52,208 [pool-2-thread-1] INFO com.rongwu.SMPPServerSimulator - Accepting bind for session 3d92afa2, interface version IF_34
2016-08-16 11:25:52,208 [EnquireLinkSender: com.rongwu.session.SMPPServerSession@4e5a309d] INFO com.rongwu.session.AbstractSession - Starting EnquireLinkSender for session 3d92afa2
2016-08-16 11:25:52,227 [pool-2-thread-1] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 19 80 00 00 09 00 00 00 00 00 00 00 01 73 79 73 00 02 10 00 01 34
2016-08-16 11:25:52,252 [pool-4-thread-2] DEBUG com.rongwu.session.PDUProcessServerTask - Received SMPP message PDUHeader(58, 00000004, 00000000, 2) 00 00 00 3a 00 00 00 04 00 00 00 00 00 00 00 02 00 00 00 31 36 31 36 00 00 00 36 32 31 36 31 36 31 36 00 00 00 00 00 00 00 00 00 00 0d 48 65 6c 6c 6f 20 30 20 69 64 78 3d 30
2016-08-16 11:25:52,374 [pool-4-thread-2] DEBUG com.rongwu.SMPPServerSimulator - Receiving submit_sm 'Hello 0 idx=0', and return message id 7bdf3eb8
2016-08-16 11:25:52,375 [pool-4-thread-2] DEBUG com.rongwu.session.state.SMPPServerSessionBoundTX - Sending response with message_id 7bdf3eb8 for request with sequence_number 2
2016-08-16 11:25:52,375 [pool-4-thread-2] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 19 80 00 00 04 00 00 00 00 00 00 00 02 37 62 64 66 33 65 62 38 00
2016-08-16 11:25:52,375 [pool-4-thread-2] DEBUG com.rongwu.SMPPServerSimulator - submit_sm_resp with message_id 7bdf3eb8 has been sent
2016-08-16 11:25:54,251 [pool-4-thread-3] DEBUG com.rongwu.session.PDUProcessServerTask - Received SMPP message PDUHeader(16, 00000006, 00000000, 3) 00 00 00 10 00 00 00 06 00 00 00 00 00 00 00 03
2016-08-16 11:25:54,252 [pool-4-thread-3] INFO com.rongwu.session.state.AbstractGenericSMPPSessionBound - Receiving unbind request
2016-08-16 11:25:54,252 [pool-4-thread-3] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 10 80 00 00 06 00 00 00 00 00 00 00 03
2016-08-16 11:25:54,257 [Thread-2] WARN com.rongwu.session.SMPPServerSession - IOException while reading: null
2016-08-16 11:25:54,257 [Thread-2] DEBUG com.rongwu.session.AbstractSession - Close session 3d92afa2
2016-08-16 11:25:54,259 [Thread-2] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPServerSession@4e5a309d,5,main]
2016-08-16 11:25:54,710 [EnquireLinkSender: com.rongwu.session.SMPPServerSession@4e5a309d] DEBUG com.rongwu.session.AbstractSession - EnquireLinkSender stopped for session 3d92afa2
2016-08-16 11:25:54,710 [Thread-2] DEBUG com.rongwu.session.AbstractSession - Session 3d92afa2 is closed and enquireLinkSender stopped
2016-08-16 11:25:54,711 [Thread-2] DEBUG com.rongwu.session.AbstractSession - Close session 3d92afa2
2016-08-16 11:25:54,711 [Thread-2] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPServerSession@4e5a309d,5,]
2016-08-16 11:25:54,711 [Thread-2] DEBUG com.rongwu.session.AbstractSession - Session 3d92afa2 is closed and enquireLinkSender stopped
2016-08-16 11:25:54,712 [Thread-2] INFO com.rongwu.session.SMPPServerSession - PDUReaderWorker stop
打印的LOG如下客户端
2016-08-15 11:38:20,280 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Target server 192.168.16.118:8056
2016-08-15 11:38:20,280 [AWT-EventQueue-0] INFO com.rongwu.StressClient - System ID: j
2016-08-15 11:38:20,281 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Password: jpwd
2016-08-15 11:38:20,281 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Source address: 1616
2016-08-15 11:38:20,281 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Destination address: 62161616
2016-08-15 11:38:20,281 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Transaction timer: 2000
2016-08-15 11:38:20,281 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Bulk size: 1
2016-08-15 11:38:20,282 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Max outstanding: 10
2016-08-15 11:38:20,282 [AWT-EventQueue-0] INFO com.rongwu.StressClient - Processor degree: 3
2016-08-15 11:38:20,332 [pool-1-thread-1] DEBUG com.rongwu.session.SMPPSession - Connect and bind to 192.168.16.118 port 8056
2016-08-15 11:38:22,328 [pool-1-thread-1] INFO com.rongwu.session.SMPPSession - Connected to /192.168.16.118
2016-08-15 11:38:22,343 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] INFO com.rongwu.session.SMPPSession - Starting PDUReaderWorker
2016-08-15 11:38:22,357 [pool-1-thread-1] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 1f 00 00 00 09 00 00 00 00 00 00 00 01 6a 00 6a 70 77 64 00 63 6c 6e 00 34 00 00 00
2016-08-15 11:38:22,929 [pool-3-thread-1] DEBUG com.rongwu.session.PDUProcessTask - Received SMPP message PDUHeader(25, 80000009, 00000000, 1) 73 79 73 00 02 10 00 01 34
2016-08-15 11:38:22,930 [pool-3-thread-1] DEBUG com.rongwu.session.state.SMPPSessionOpen - Bind Response header (25, 80000009, 00000000, 1)
2016-08-15 11:38:22,984 [pool-1-thread-1] DEBUG com.rongwu.session.AbstractSession - bind response received for session 34762440
2016-08-15 11:38:22,984 [pool-1-thread-1] INFO com.rongwu.session.SMPPSession - Other side reports SMPP interface version 34
2016-08-15 11:38:22,984 [pool-1-thread-1] INFO com.rongwu.session.SMPPSession - Changing processor degree to 3
2016-08-15 11:38:22,985 [pool-1-thread-1] INFO com.rongwu.StressClient - Bound to 192.168.16.118:8056
2016-08-15 11:38:22,985 [EnquireLinkSender: com.rongwu.session.SMPPSession@f231023] INFO com.rongwu.session.AbstractSession - Starting EnquireLinkSender for session 34762440
2016-08-15 11:38:22,986 [pool-1-thread-1] INFO com.rongwu.StressClient - Starting to send 1 bulk messages
2016-08-15 11:38:22,986 [Thread-0] INFO com.rongwu.StressClient - Starting traffic watcher...
2016-08-15 11:38:22,992 [pool-2-thread-1] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 3a 00 00 00 04 00 00 00 00 00 00 00 02 00 00 00 31 36 31 36 00 00 00 36 32 31 36 31 36 31 36 00 00 00 00 00 00 00 00 00 00 0d 48 65 6c 6c 6f 20 30 20 69 64 78 3d 30
2016-08-15 11:38:23,974 [pool-3-thread-2] DEBUG com.rongwu.session.PDUProcessTask - Received SMPP message PDUHeader(25, 80000004, 00000000, 2) 33 33 38 64 34 39 37 38 00
2016-08-15 11:38:23,976 [pool-2-thread-1] DEBUG com.rongwu.session.AbstractSession - submit_sm response received for session 34762440
2016-08-15 11:38:23,987 [Thread-0] INFO com.rongwu.StressClient - Request/Response per second: 1/1 of 1 maxDelay=990
2016-08-15 11:38:24,989 [pool-1-thread-1] INFO com.rongwu.StressClient - Done
2016-08-15 11:38:24,990 [pool-1-thread-1] DEBUG com.rongwu.session.AbstractSession - Unbind and close sesssion 34762440
2016-08-15 11:38:24,992 [pool-1-thread-1] DEBUG com.rongwu.DefaultPDUSender - Sending SMPP message 00 00 00 10 00 00 00 06 00 00 00 00 00 00 00 03
2016-08-15 11:38:26,057 [pool-3-thread-3] DEBUG com.rongwu.session.PDUProcessTask - Received SMPP message PDUHeader(16, 80000006, 00000000, 3)
2016-08-15 11:38:26,058 [pool-1-thread-1] DEBUG com.rongwu.session.AbstractSession - unbind response received for session 34762440
2016-08-15 11:38:26,058 [pool-1-thread-1] DEBUG com.rongwu.session.AbstractSession - Close session 34762440
2016-08-15 11:38:26,059 [pool-1-thread-1] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPSession@f231023,5,main]
2016-08-15 11:38:26,060 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] WARN com.rongwu.session.SMPPSession - IOException while reading: Socket closed
2016-08-15 11:38:26,061 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] DEBUG com.rongwu.session.AbstractSession - Close session 34762440
2016-08-15 11:38:26,062 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPSession@f231023,5,main]
2016-08-15 11:38:26,489 [EnquireLinkSender: com.rongwu.session.SMPPSession@f231023] DEBUG com.rongwu.session.AbstractSession - EnquireLinkSender stopped for session 34762440
2016-08-15 11:38:26,491 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] DEBUG com.rongwu.session.AbstractSession - Session 34762440 is closed and enquireLinkSender stopped
2016-08-15 11:38:26,491 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] DEBUG com.rongwu.session.AbstractSession - Close session 34762440
2016-08-15 11:38:26,491 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPSession@f231023,5,]
2016-08-15 11:38:26,492 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] DEBUG com.rongwu.session.AbstractSession - Session 34762440 is closed and enquireLinkSender stopped
2016-08-15 11:38:26,492 [PDUReaderWorker: com.rongwu.session.SMPPSession@f231023] INFO com.rongwu.session.SMPPSession - PDUReaderWorker stop
2016-08-15 11:38:26,491 [pool-1-thread-1] DEBUG com.rongwu.session.AbstractSession - Session 34762440 is closed and enquireLinkSender stopped
2016-08-15 11:38:37,006 [Finalizer] DEBUG com.rongwu.session.AbstractSession - Close session 34762440
2016-08-15 11:38:37,010 [Finalizer] INFO com.rongwu.session.AbstractSession - Closing enquireLinkSender for session Thread[EnquireLinkSender: com.rongwu.session.SMPPSession@f231023,5,]
2016-08-15 11:38:37,010 [Finalizer] DEBUG com.rongwu.session.AbstractSession - Session 34762440 is closed and enquireLinkSender stopped