This article is the second part of sending text messages. Here we introduce how to limit the frequency of sending text messages to the same user (based on mobile phone number and IP).
1. Use session
If it is a web program, it is also possible to record the last time sent in the session, but it can be bypassed. The simplest thing is to directly restart the browser or clear cache and other data that can mark the session, so the records in the session can be bypassed. Although many people are not professional in computers and have not learned these. But we need to note that the reason for limiting the sending frequency is to prevent "SMS bombs", that is, someone maliciously and frequently requests to send text messages to a mobile phone number. Therefore, this person may understand this knowledge.
Next, we use the "global" data limit to send frequency to the same user. Let's do some "preparation" work first.
2. Define interfaces and entity classes
The entity classes we need are as follows:
SmsEntity.java
public class SmsEntity{ private Integer id; private String mobile; private String ip; private Integer type; private Date time; private String captcha; // Omit the constructor method and getter and setter methods}The filtering interface is as follows:
SmsFilter.java
public interface SmsFilter { /** * Initialize the filter*/ void init() throws Exception; /** * Determine whether the text message can be sent. * @param smsEntity The content of the text message to be sent* @return If it can be sent, it will return true, otherwise it will return false */ boolean filter(SmsEntity smsEntity); /** * Destroy the filter*/ void destroy();}3. Main code
To limit the frequency of sending, you need to record a certain mobile phone number (IP) and the time of the last time you sent a text message. It is very suitable for Map to complete. Here we first use ConcurrentMap to implement it:
FrequencyFilter.java
public class FrequencyFilter implements SmsFilter { /** * Send interval, unit: milliseconds*/ private long sendInterval; private ConcurrentMap<String, Long> sendAddressMap = new ConcurrentHashMap<>(); // Some useless code omitted @Override public boolean filter(SmsEntity smsEntity) { if(setSendTime(smsEntity.getMobile()) && setSendTime(smsEntity.getIp())){ return true; } return false; } /** * Modify the sending time to the current time. * If the time interval from the last sent is greater than {@link #sendInterval}, set the sending time to the current time. Otherwise, no content will be modified. * * @param id Send mobile number or ip * @return If the sending time is successfully modified to the current time, it will return true. Otherwise, it will return false */ private boolean setSendTime(String id) { long currentTime = System.currentTimeMillis(); Long sendTime = sendAddressMap.putIfAbsent(id, currentTime); if(sendTime == null) { return true; } long nextCanSendTime = sendTime + sendInterval; if(currentTime < nextCanSendTime) { return false; } return sendAddressMap.replace(id, sendTime, currentTime); }}Here, the main logic is implemented in the setSendTime method :
Lines 25-28: First, assuming that the user sends a SMS for the first time, then the current time should be put into the sendAddressMap. If putIfAbsent returns null, it means that the user is indeed sending a SMS for the first time, and the current time has been put into the map, and it can be sent.
Lines 30-33: If the user is not sending a SMS for the first time, then it is necessary to determine whether the time and current interval of sending the SMS last time and the time interval are less than the sending time interval. If it is less than the sending time interval, then it cannot be sent.
Line 35: If the time interval is large enough, then you need to try to set the sending time to the current time.
1) Then you can repeat lines 25-35 to ensure that they are absolutely correct.
2) You can also directly think that it cannot be sent, because although the theoretical time for "execution of lines 26-35" may be greater than the "send interval", how much is the probability? It can basically be ignored.
This code implements frequency limitation, but if there is only "in" but not "out", then the content occupied by sendAddressMap will become larger and larger until an OutOfMemoryError exception is generated. Next, we will add the code to clean up expired data regularly.
4. Clean up expired data
FrequencyFilter.java
/** * Based on the above code, add the following code*/public class FrequencyFilter implements SmsFilter { private long cleanMapInterval; private Timer timer = new Timer("sms_frequency_filter_clear_data_thread"); @Override public void init() { timer.schedule(new TimerTask() { @Override public void run() { cleanSendAddressMap(); } }, cleanMapInterval, cleanMapInterval); } /** * Delete all expired data in sendAddressMap*/ private void cleanSendAddressMap() { long currentTime = System.currentTimeMillis(); long expireSendTime = currentTime - sendInterval; for(String key : sendAddressMap.keySet()) { Long sendTime = sendAddressMap.get(key); if(sendTime < expireSendTime) { sendAddressMap.remove(key, sendTime); } } } @Override public void destroy() { timer.cancel(); }}This program is not complicated. Start a timer and execute the cleanSendAddressMap method every milliseconds of cleanMapInterval to clean up expired data.
The cleanSendAddressMap method first gets the current time and obtains a time value based on the current time: All text messages are sent after this time, SMS cannot be sent again. Then delete all key-value pairs with value smaller than this time value from the entire map.
Of course, after adding the above code, the initial code has another bug: When the last line sendAddressMap.replace(id, sendTime, currentTime) fails to execute, it is not necessarily that other threads have been replaced, but it is also possible that the cleaning thread has deleted the data. So we need to modify the last few lines of the setSendTime method:
FrequencyFilter.java
private boolean setSendTime(String id) { // Omit the previous code if(sendAddressMap.replace(id, sendTime, currentTime)) { return true; } return sendAddressMap.putIfAbsent(id, currentTime) == null;}If the replacement is successful here, then return true directly.
If the replacement is not successful, then it may be that other threads have been replaced first (in the first case); it may also be that they have been deleted by the cleaned thread (in the second case); it may even be that they have been deleted by the cleaned thread first, and other threads have inserted new time values (in the third case).
At this point, the code that limits the sending time is completed. Of course, this program has a small bug or "feature":
If a customer with IP "192.168.0.1" requests to send a text message to the mobile phone number "12345678900", and then requests to send a text message to the mobile phone number "12345678900" within sendInterval on the machine with IP "192.168.0.2". Then the text message will not be sent, and the last time of the mobile phone number "12345678900" is set to the current time.
5. Examples of use
Below we provide a Server layer to show how to integrate the code in the previous article and this article:
SmsService.java
public class SmsService{ private Sms sms; private List<SmsFilter> filters; private Properties template; // Some codes are omitted/** * Send verification code* * @param smsEntity Basic data for sending text messages* @return If the submission is successful, return 0. Otherwise, return other values. */ public int sendCaptcha(SmsEntity smsEntity){ for(SmsFilter filter : filters) { if(!filter.filter(smsEntity)){ return 1; } } if(SmsEntity.REGISTER_TYPE.equals(smsEntity.getType())) { sendRegisterSms(smsEntity); } else{ return 2; } return 0; } /** * Send registration verification code* * @param smsEntity Basic data for sending text messages*/ private void sendRegisterSms(SmsEntity smsEntity) { sms.sendMessage(smsEntity.getMobile(), template.getProperty("register").replace("{captcha}", smsEntity.getCaptcha())); }}Then, "inject" FrequencyFilter and AsyncSmsImpl in the previous article through the set method.
The above is all about this article, I hope it will be helpful for everyone to learn Java programming.