cloudhopper-smpp
cloudhopper-smpp copied to clipboard
concatenating sms parts to a long sms from UDH
Is there any code implementing concatenation of a list of single sms parts based on UDH into long messages (what that normally mobile phones do)?
P.S. may be considered as an off-topic issue but found it a good idea to ask in related repositories
Hello behard,
Below code snippet will return Message Parts Into Byte Array
private static final int MAX_MULTIPART_MSG_SEGMENT_SIZE_UCS2 = 134;
private static final int MAX_SINGLE_MSG_SEGMENT_SIZE_UCS2 = 70;
private static final int MAX_MULTIPART_MSG_SEGMENT_SIZE_7BIT = 153;
private static final int MAX_SINGLE_MSG_SEGMENT_SIZE_7BIT = 160;
public static byte[][] getParts(String messageBody, String charSet) {
int maximumSingleMessageSize = 0;
int maximumMultipartMessageSegmentSize = 0;
byte[] byteSingleMessage = null;
if (! CharsetUtil.NAME_UCS_2.equals(charSet)) {
byteSingleMessage = CharsetUtil.encode(messageBody, charSet);
maximumSingleMessageSize = MAX_SINGLE_MSG_SEGMENT_SIZE_7BIT;
maximumMultipartMessageSegmentSize = MAX_MULTIPART_MSG_SEGMENT_SIZE_7BIT;
} else {
byteSingleMessage = CharsetUtil.encode(messageBody, charSet);
maximumSingleMessageSize = MAX_SINGLE_MSG_SEGMENT_SIZE_UCS2;
maximumMultipartMessageSegmentSize = MAX_MULTIPART_MSG_SEGMENT_SIZE_UCS2;
}
byte[][] byteMessagesArray = null;
if (messageBody.length() > maximumSingleMessageSize) {
// split message according to the maximum length of a segment
byteMessagesArray = splitUnicodeMessage(byteSingleMessage, maximumMultipartMessageSegmentSize);
// set UDHI so PDU will decode the header
// esmClass = new ESMClass(MessageMode.DEFAULT, TrayIcon.MessageType.DEFAULT, GSMSpecificFeature.UDHI);
} else {
byteMessagesArray = new byte[][] { byteSingleMessage };
// esmClass = new ESMClass();
}
return byteMessagesArray;
}
private static byte[][] splitUnicodeMessage(byte[] aMessage, Integer maximumMultipartMessageSegmentSize) {
final byte UDHIE_HEADER_LENGTH = 0x05;
final byte UDHIE_IDENTIFIER_SAR = 0x00;
final byte UDHIE_SAR_LENGTH = 0x03;
// determine how many messages have to be sent
int numberOfSegments = aMessage.length / maximumMultipartMessageSegmentSize;
int messageLength = aMessage.length;
if (numberOfSegments > 255) {
numberOfSegments = 255;
messageLength = numberOfSegments * maximumMultipartMessageSegmentSize;
}
if ((messageLength % maximumMultipartMessageSegmentSize) > 0) {
numberOfSegments++;
}
// prepare array for all of the msg segments
byte[][] segments = new byte[numberOfSegments][];
int lengthOfData;
// generate new reference number
byte[] referenceNumber = new byte[1];
new Random().nextBytes(referenceNumber);
// split the message adding required headers
for (int i = 0; i < numberOfSegments; i++) {
if (numberOfSegments - i == 1) {
lengthOfData = messageLength - i * maximumMultipartMessageSegmentSize;
} else {
lengthOfData = maximumMultipartMessageSegmentSize;
}
// new array to store the header
segments[i] = new byte[6 + lengthOfData];
// UDH header
// doesn't include itself, its header length
segments[i][0] = UDHIE_HEADER_LENGTH;
// SAR identifier
segments[i][1] = UDHIE_IDENTIFIER_SAR;
// SAR length
segments[i][2] = UDHIE_SAR_LENGTH;
// reference number (same for all messages)
segments[i][3] = referenceNumber[0];
// total number of segments
segments[i][4] = (byte) numberOfSegments;
// segment number
segments[i][5] = (byte) (i + 1);
// copy the data into the array
System.arraycopy(aMessage, (i * maximumMultipartMessageSegmentSize), segments[i], 6, lengthOfData);
}
return segments;
}
You can loop generated byte Array to submit each part to connected smsc
Thanks
thank you @gruntday but I was looking for the reverse operation code, I have a list of message parts and I want to construct long sms bodies from them :)
take a look on these methods:
com.cloudhopper.smpp.util.SmppUtil#isUserDataHeaderIndicatorEnabled
com.cloudhopper.commons.gsm.GsmUtil#getShortMessageUserDataHeader
com.cloudhopper.commons.gsm.GsmUtil#getShortMessageUserData
using them, you can get short messages without UDH, and then you just join them.
@behrad do you have the reference number and total parts and current seq per each message what i mean how current parts saved at your side ? so that i can give you exact example for concatenation
do you have the reference number and total parts and current seq per each message what i mean how current parts saved at your side ? so that i can give you exact example for concatenation
they are standard smpp parts, the information you mean is in UDH headers.
Hi
I am trying to solve the same problem.
@krasa - thanks for pointing to the GSM Utils methods.
Using getShortMessageUserData and decoding based on the the relevant coding scheme I can get the message information.
But to get the information from the User Data Header (i.e. number of total messages, message id, number of this message) I am battling.
getShortMessageUserDataHeader returns a byte[] which I don't know how to parse to get the information.
Is there any example code for processing the User Data Header in cloudhopper?
In the provided example code I don't see any parsing of the actual user data header to get the information.
Hello @gruntday,
Apologies for resurrecting this thread.
I have a question about the example code you provided.
For the 7-bit encoding use case, the code example doesn't apply any padding bits between the UDH and the message.
The GSM specs [3GPP TS 23.040 section 9.2.3.24] indicate that for 7-bit character encoding, padding bits should added so that the short message starts at a septet boundary.
Therefore my question is, do padding bits need to be added between the UDH and the encoded bytes?
Or, because the 7-bit characters are packed into octets, can we ignore the padding bits?
Cheers, Jaimie
@JaimieW The specification is correct, you need those padding bits - I had a problem with that once.
@krasa Thanks for that. A followup if I may. If I'm using the cloudhopper PackedGSMCharset to encode the short message, the message is packed into octets. Where do the padding bits go?
I have the UDH determined which is in octets. I have the message encoded in 7-bit characters packed into octets via the PackedGSMCharset.encode method.
I'm really struggling to see how to add the padding bits. Any advice/tips would be appreciated.
Cheers, Jaimie
Hi @JaimieW
Not sure if this will help - I battled to get the UDH 7 bit packing sorted and eventually modified @gruntday provided method to be like this - using PduBitPacker static class. Seems a bit of a hack but it works when integrating with out SMPP transit partner.
private static byte[][] splitMultipartMessage(byte[] aMessage) throws Exception {
int maximumMultipartMessageSegmentSize = 134;
// generate new reference number
byte[] referenceNumber = new byte[1];
new Random().nextBytes(referenceNumber);
final byte UDHIE_HEADER_LENGTH = 0x05;
final byte UDHIE_IDENTIFIER_SAR = 0x00;
final byte UDHIE_SAR_LENGTH = 0x03;
// determine how many messages have to be sent
log.debug("Splitting message up: [{}]", aMessage);
int numberOfSegments = aMessage.length / maximumMultipartMessageSegmentSize;
int messageLength = aMessage.length;
if (numberOfSegments > 255) {
numberOfSegments = 255;
messageLength = numberOfSegments * maximumMultipartMessageSegmentSize;
}
if ((messageLength % maximumMultipartMessageSegmentSize) > 0) {
numberOfSegments++;
}
// prepare array for all of the msg segments
byte[][] segments = new byte[numberOfSegments][];
int lengthOfData;
// split the message adding required headers
for (int i = 0; i < numberOfSegments; i++) {
if (numberOfSegments - i == 1) {
lengthOfData = messageLength - i * maximumMultipartMessageSegmentSize;
} else {
lengthOfData = maximumMultipartMessageSegmentSize;
}
//prepend 0x00 to aMessage
//bit pack aMessage
byte[] bMessage = new byte[lengthOfData + 1];
bMessage[0] = 0x00;
System.arraycopy(aMessage, (i * maximumMultipartMessageSegmentSize), bMessage, 1, lengthOfData);
//now pack bmessage using gsm 7 bit
byte[] messageBytes = PduBitPacker.PackBytes(bMessage, 1);
// new array to store the header
segments[i] = new byte[6 + messageBytes.length - 1 /*remove 1 byte of bit padding*/];
// UDH header
// doesn't include itself, its header length
segments[i][0] = UDHIE_HEADER_LENGTH;
// SAR identifier
segments[i][1] = UDHIE_IDENTIFIER_SAR;
// SAR length
segments[i][2] = UDHIE_SAR_LENGTH;
// reference number (same for all messages)
segments[i][3] = referenceNumber[0];
// total number of segments
segments[i][4] = (byte) numberOfSegments;
// segment number
segments[i][5] = (byte) (i + 1);
// copy the data into the array
System.arraycopy(messageBytes, 0, segments[i], 6, messageBytes.length - 1 /*remove 1 byte of bit padding*/);
break;
}
}
return segments;
}
Also to answer my original question in case anyone else is battling with this:
getShortMessageUserDataHeader returns a byte[] which I don't know how to parse to get the information.
Is there any example code for processing the User Data Header in cloudhopper?
I managed to sort this as follows:
byte[] userDataHeader = GsmUtil.getShortMessageUserDataHeader(baseSm.getShortMessage());
int thisMessageId = userDataHeader[3] & 0xff; // Range 0 to 255, not -128 to 127;
int totalMessages = userDataHeader[4] & 0xff; // Range 0 to 255, not -128 to 127;
int currentMessageNum = userDataHeader[5] & 0xff; // Range 0 to 255, not -128 to 127;