SMPP分析

    xiaoxiao2026-04-03  7

    因业务需要,需要开发一个引擎(采用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);         }     }

    开启了一个线程来读取5.1connectAndBind方法里,sendBind的SMPP包。 查看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

    测试程序下载

    转载请注明原文地址: https://ju.6miu.com/read-1308464.html
    最新回复(0)