Use Tomcat to implement Comet chat rooms based on iframe streaming
First, no picture, no truth, first:
This is a chat room Demo Based On Comet. Its function is similar to QQ chat. If there is a new message in the chat process, the server needs to push the message to the browser, So Comet technology can be used here.
Comet generally has two implementation methods: long-polling (long-polling) and streaming ). In this article, the Demo is implemented based on streaming, and the front end uses a hidden iframe, which is also a common method. However, the progress on the browser is always changing due to the use of the iframe stream. This is because the iframe is always loading. Do not care about these details first.
Tomcat provides Comet-related APIs. Using Servlet to implement the CometProcessor interface can easily implement Comet.
1. Preparations
1.1 first, you must configure the Tomcat connection to NIO. Otherwise, Tomcat Comet cannot be used.
In the conf/server. xml directory of Tomcat, the protocol is changed to org. apache. coyote. http11.Http11NioProtocol:
After configuration, start Tomcat as follows:
1.2. Tomcat's catalina. jar package must be used during the development process, under the Tomcat lib directory. When the program runs in Tomcat, remove it.
2. Java background
2.1. CometServlet
This Servlet is a Servlet that processes Comet Http persistent connections. this Servlet implements the CometProcessor interface provided by Tomcat and uses the event method to process multiple events in the Http persistent connection cycle:
BEGIN event: There is a new HTTP connection;
END event: the connection is closed, for example, the browser is closed;
ERROR Event: Connection ERROR, such as timeout.
Events are described in more detail in the official Tomcat documentation with: http://tomcat.apache.org/tomcat-7.0-doc/aio.html
Public class CometServlet extends HttpServlet implements CometProcessor {// All HTTP persistent connections waiting for response private ArrayList
Connections = null; // The message sending thread private MessageSender messageSender = null; // start the message processing thread public void init () {connections = new ArrayList
(); MessageSender = new MessageSender (connections); Thread messageSenderThread = new Thread (messageSender); messageSenderThread. start ();} public void event (CometEvent event) throws IOException, ServletException {HttpServletResponse response = event. getHttpServletResponse (); response. setCharacterEncoding ("UTF-8"); if (event. getEventType () = CometEvent. eventType. BEGIN) {System. out. println ("BEGIN"); // a string greater than 1024 cached by PrintWriter out = response. getWriter (); StringBuilder sb = new StringBuilder (); for (int I = 0; I <1024; I ++) {sb. append ('A');} out. println ("
"); // Add the HTML comment out. flush (); synchronized (connections) {connections. add (response); System. out. println ("Current online user:" + connections. size () ;}} else if (event. getEventType () = CometEvent. eventType. ERROR) {System. out. println ("ERROR"); synchronized (connections) {connections. remove (response); System. out. println ("Current online user:" + connections. size ();} event. close ();} else if (event. getEventType () = CometEvent. eventType. END) {System. out. println ("END"); synchronized (connections) {connections. remove (response); System. out. println ("Current online user:" + connections. size ();} event. close ();}}}
In this Servlet, ArrayList Connections is used to save the HTTP persistent connection waiting for response. It is an HttpServletResponse object and can be understood as all online users. In the BEGIN event, a connection is added to connections, and the corresponding connection is deleted in the END and ERROR events.
When the Servlet initializes init, it starts a thread to process chat messages and passes connections over.
In the BEGIN event, a string with a length greater than 1024 is output through the response output stream, which is due to the browser cache, if not, Some browsers will wait until the stream is written to a certain number of bytes before it is displayed. This string has no practical significance, so you can write anything at will, but do not forget to add HTML comments.
2.2. MessageSender
MessageSender is a thread that processes chat messages and implements the Runnable interface. When new chat information is available, it immediately sends the information to all connected clients through the output stream of HttpServletResponse. If no new information is available, it is blocked.
The blocking queue ArrayBlockingQueue in java. util. concurrent is used to process chat messages. The ArrayBlockingQueue. take () method is used to obtain and remove an element from the queue. When the queue is empty, this method blocks the current thread until other threads add new elements to the queue. Of course, wait/notify can also be used here.
In fact, it can be understood as a producer consumer problem. When a user sends a message to the server, it is equivalent to producing a message, and this thread sends the message to the user, which is equivalent to consuming a message, this blocking queue is a buffer zone.
Public class MessageSender implements Runnable {// All HTTP persistent connections waiting for response private ArrayList
Connections; // The public static ArrayBlockingQueue set of messages not sent to the client
Messages = new ArrayBlockingQueue
(10); public MessageSender (ArrayList
Connections) {this. connections = connections;} public void run () {while (true) {// gets a message from the message blocking queue. If the queue is empty, the String message is blocked; try {message = messages. take ();} catch (InterruptedException e) {e. printStackTrace ();} // send the message synchronized (connections) {for (HttpServletResponse response: connections) {try {PrintWriter out = response. getWriter (); // output a script. Call JS to display the message out on the page. println ("<script> parent. addMsg ('"+ message +"
') </Script> "); out. flush ();} catch (IOException e) {e. printStackTrace ();}}}}}}
2.3. AjaxMessageServlet
This Servlet is used to process the user's request for sending information. This is a common Http request rather than a persistent connection. When you click the "send" button on the page, the chat information will be submitted to the Servlet through Ajax.
When receiving a new message, add a data entry to put in the blocked queue ArrayBlockingQueue in MessageSender. When there is new data and the queue is not empty, the MessageSender thread is no longer blocked and will immediately send the message to the client browser. This is equivalent to notifying the MessageSender thread to send a message to the client.
Public class AjaxMessageServlet extends HttpServlet {public void doPost (HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {request. setCharacterEncoding ("UTF-8"); try {// This is equivalent to notifying the MessageSender thread to send messages to the client MessageSender. messages. put ("[" + request. getParameter ("name") + "]:" + request. getParameter ("msg");} catch (InterruptedException e) {e. printStackTrace () ;}} public void doGet (HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {doPost (request, response );}}
3. Web Front-end
<Script type = "text/javascript"> // append a message to HTML. This function is used to call function addMsg (msg) to the javascript script added to iframe) {var msgElement = document. getElementById ("msg"); msgElement. innerHTML + = msg;} // click the "send" button and then Ajax sends the message function sendMsg () {var xmlhttp = new XMLHttpRequest (); xmlhttp. open ("POST", "sendMsg"); // sendMsg is the URLxmlhttp corresponding to AjaxMessageServlet. setRequestHeader ("Content-type", "application/x-www-form-urlencoded"); var name = document. getElementById ("input-name "). value; var msg = document. getElementById ("input-msg "). value; xmlhttp. send ("name =" + encodeURIComponent (name) + "& msg =" + encodeURIComponent (msg); document. getElementById ("input-msg "). value = "";} // reload iframefunction iframeRefresh () {var iframeElement = document. getElementById ("iframe"); iframeElement. src = iframeElement. src ;}</script>
<Iframe id = "iframe" style = "display: none;" src = "comet" onload = "iframeRefresh ();"> </iframe> Name:
Message:
In JS, The addMsg function is provided to the js Script output in MessageSender to call and display messages on the page.
The sendMsg function is a "send" button click event that sends the chat information to AjaxMessageServlet.
The iframeRefresh function reload the iframe when the server times out. The timeout function times out for the server and the load is complete for the client. Therefore, it is called in the onload of the iframe. You can set the timeout time to use the event in the BEGIN event. setTimeout (30*1000) or event. getHttpServletRequest (). setAttribute ("org. apache. tomcat. comet. timeout ", new Integer (30*1000.
Iframe on the page is set to display: none, that is, it is not displayed. src is the URL corresponding to CometServle. When there is new information, MessageSender will output a piece of JS to iframe:
Out. println ("<script> parent. addMsg ('" + message +"
') </Script> ");
After the browser loads this JavaScript code, it will run immediately and call the addMsg function to display the information on the page.
4. Source Code
If you need the DEMO source code, leave email in the reply.
Author: Cross brother reprint please indicate the source: http://blog.csdn.net/xiao__gui/article/details/38487117