Client chat room construction ideas exclusive sharing

Source: Internet
Author: User

The principle of promotion has been determined, now see the concrete implementation. With respect to server-side implementations, the implementation of the client chat room can be trickier. Because the client chat room is not like a web chat room, how to develop, the server-side development degrees of freedom is quite large, although a bit exaggerated, but as long as you want to use the core code is not allowed, but the client is very different. Here, the space is limited to a narrow scope, the implementation of the best use only HTML, which can be implemented in a refresh-based chat room, but in a push-based chat room is almost impossible. In order to implement it, it is necessary to relax the restrictions on the client implementation space, it is essential to use the active script, or the use of IE JScript, combined with its DOM (Document Object Model), to implement a "client" embedded ie.

In IE, a page can be "unfinished" (pending), when the URL of a page is submitted, as the server-side data continues to arrive, the state of the page (ReadyState property) has been "interactive", that is, can be interactive. At this point its dom has been initialized and can be accessed. The connection used by this page serves as a push channel, and information can be pushed continuously onto the page.

Unfortunately, IE does not provide an event that notifies you that data arrives in an unfinished page, and there may be three events related to this: "onReadyStateChange", "ondataavailable", and "Onpropertychange" are not available here or are unavailable. Thus, a timer has to be set up to detect if new data arrives. Unlike the timed refresh mechanism used by update-based client chat rooms, the detection is entirely on the client side, it detects local pages, and the update-based client chat room detects the server. Therefore, timing detection here does not add to the burden on the network and the server. The accuracy of the timer will affect the delay shown on the user interface, where the interval is set to 0.5 seconds, and the delay caused by this interval is acceptable and does not burden the client processor.

Dom is a good way to manage documents, and in order to make it easier for clients to detect new information using the DOM, each message pushed by the server is embedded in a pair of HTML tags (the "" tag is used here because it occupies less space). Depending on the Microsoft documentation, using the DOM, you can get a collection of tokens from a specified root, each of which is a tag of that type, with a collection of attributes that indicate the number of elements within the collection-"Length". Each element can be positioned by its index in the collection, and the index is incremented by the markup of the element that corresponds to its position in the HTML document, at an interval of 1.

The client always records the number of tokens at the time of the last retrieval, and when a timer tick arrives, it is possible to determine if a new message arrives, as long as the number of marks at that time is detected, relative to the number of previous records. And because the index is incrementally allocated, the index of the new information can be predicted, according to its index can be removed from the collection, for each (possibly in the two time interval to reach more than one piece of information) out of the tag, can be extracted from its innerHTML property message content, and then sent to script analysis.

Since the push channel is essentially a TCP connection, there is no guarantee that it will not be interrupted and must try to cope with an unexpected interruption in the push channel, otherwise the user will not receive any information once the interrupt occurs. The method of handling unexpected interrupts is simple, and once the connection is interrupted, the ReadyState property of the page becomes complete, as long as the value of the property is detected and the actual state of the client is used to determine whether an unexpected interrupt has occurred. Once the interrupt occurs, the client script should automatically resubmit the page in order to get through the push channel again. The client tag counter should also be reset at this time.

For each message sent to the analysis, depending on its content, the script does the appropriate action, interacting with the user, such as displaying a statement, or removing the user from the member list because a user exits the client chat room. Thus the entire user interface body is dynamically generated by the script based on the current state. To use a script to generate page content, you still use the DOM, use the CreateElement method of the built-in document object, and the AppendChild method of each tag, the InsertBefore method, and the RemoveChild method to manipulate the DOM tree. The results of the operation will be displayed in real time on the user interface. The reason for not inserting tags directly with the innerHTML attribute is because of its low performance, significant latency, and the convenience of DOM.

Another task that the script is responsible for is to interact with the server, and the user's actions such as speaking, entering, and exiting are handled by the script, because it is impossible (or inconvenient) for the user to interact directly with the server. There are many ways to interact with the server, and the simplest is to use forms (form). However, submitting a form refreshes its page, and a push-based chat room is not required or should not be refreshed. If you want to use a form, you must embed one or more iframe, inside it to load another page containing the required form, use a script to copy the user's input from the main page to the forms, and then submit them by calling the form's Submit method. At this point, it is similar to the implementation of the problem, the main page does not know whether its submitted pages have been returned, then the need for timed detection, which will increase the complexity of the implementation, but also bring more instability factors. Another way to do this is to embed a script on each returned page, hook up the OnLoad event for the page, and then notify the main page by invoking a cross-frame method to load completion. But these methods are not ideal. The best approach is to use IE5 's newly supported XMLHTTP components. The ProgID of the component is "Microsoft.XMLHTTP", which, as its name, primarily operates on XML data. It is important that it supports asynchronous operations and callback functions that can be used to interact with the server in a purely scripted manner. In the ointment, there is no reference to its XMLHTTP object in the context of the callback function, and other methods, such as global variables, must be used to obtain the object. It would be nice if Microsoft could have the callback function take a parameter that references the XMLHTTP object it was attached to, or let the this variable refer to the object.

The details about using scripts to manipulate the UI to interact with the user and to interact with the server using scripts are not the focus of this article, and are not discussed here.

Server-side on the server side, the most important thing is to implement a push mechanism that conforms to the above criteria. The most notable feature of this mechanism is asynchrony, which often, or most of the time, is not working, only becomes active when new information arrives, and sends the new information to the user.

Windows is a well-supported operating system for asynchronous operations, which naturally reminds you to use multiple threads, each thread is responsible for a user connection, most of the time is suspended, and when new information arrives, it wakes up with some mechanism and then pushes the data for the user. As a result, it seems that just write a server-side process that uses multithreading to handle user requests. In reality, however, the client is not just receiving information pushed by the server, but also interacting with the server, such as sending a user's speech to the server. Obviously, the simplest way to deal with these interactions is to use the Asp,asp scripting feature to easily analyze and process user requests, organize and generate responses, and be flexible and easy to maintain. However, the ASP does not have the ability to complete the server push, it is not designed to handle a large number of asynchronous operations. In order to be able to fish and bear, it is necessary to combine the two, in all let ASP and binary code can exchange visits mechanism, the best is Microsoft's COM model, ASP fully support COM, as long as the binary code to implement COM, it can be good interaction with the ASP.

Now that COM is selected, the executable and threading models are selected, and for performance, the best way is to make the component a DLL and load it into the IIS process (which is actually the child process of IIS, DLLHOST. EXE) is called directly in the address space, eliminating the need for marshaling across process boundaries, thus improving performance. Since the use of DLLs, the implementation of the push part is also bound to do the DLL, otherwise, if the implementation of the push part of the other process, the DLL is tantamount to a proxy, interprocess communication is still unavoidable, the DLL form of the components brought about by the performance of the upgrade will be offset. Since the push part has been integrated into the component, and the implementation of the push part must be multi-threaded, that is, the whole program using multithreading is inevitable, more on the performance of consideration, components should undoubtedly choose a multithreaded model, that is, free or both. Now, by design, implementing the push code will be implemented in the IIS process, and there are many ways to implement push in the code that runs within the IIS process, but the best way to do this is to use the ISAPI interface of IIS. Using this interface, the application interacts directly with IIS, not the client, and IIS is responsible for the management of thread and client connections, as well as the sending and receiving of information and some analysis work. This can significantly reduce the amount of code, reduce development complexity, and more closely integrate with the Web server, improve overall performance.

The ISAPI part refers to the ISAPI extension (Extension), which, as the implementation part of the push, can request its service by using the normal request. dll. You can also configure it as a scripting engine, map a file extension to the engine, and let it control a sub-zone, where a user can request a service by requesting an arbitrary name file with that extension under that zone.

In this way, the DLL is actually two parts, one that appears as a component interacting with the ASP, and another as an ISAPI extension that interacts with IIS. From the point of view of IIS, the two parts are loaded and initialized separately-each one being called the first time, except that the same DLL is loaded. Because there may be cases in the same DLL, but some are already working, and the other part is not initialized, the synchronization of these two parts should be considered at design time.

Next design server-side structure, the server-side design focus on the server side to achieve the highest performance. One of the most critical is the rational use of multithreaded technology. In addition, try to use the functionality already available from the operating system to simplify program implementation. For example, the operating system chosen here is that Windows 2000,windows 2000 has built-in the implementation of a hash table, so there is no need to write your own hash table when using a large number of strings.

The client chat room component should be able to support multiple chat rooms, rather than individuals, to categorize chat content. Thus requires a "hall", the user just log in to the client chat room, in the lobby, where you can view the status of each chat room, decide which chat room to enter. In order to manage each open chat room, a class is required to describe those open chat rooms, which are called chat room descriptors. Similarly, in order to manage each online user, a class called a user descriptor is also required to describe them. Finally, in order to manage multiple instances of these client chat rooms and user descriptors, a total directory is required to index them.

Because client-side chat room descriptors and user descriptors are multi-instance, especially user descriptors, which are frequently allocated and released during operation, it is very easy to produce memory fragmentation, so it is necessary to use special management methods for them. By using their own private heap for these classes, you can solve the fragmentation problem.

When the user logs on, it immediately initiates a connection to the server, the ISAPI portion of the connection request component, the ISAPI section verifies the user's identity, finds the user's descriptor, and then hooks the requested ECB (Extension Control Block) as a push channel to the descriptor. The last function returns pending to IIS, indicating that processing is incomplete.

When the end user speaks, the content is transmitted to the server, IIS will request to the ASP to process, the ASP to authenticate the user, the request to do some analysis and processing, through the interface of the COM component, call the appropriate method, notify the component of a user to speak. The component checks the user's location and sends the statement to each user in the chat room where it is located, by invoking the ISAPI interface of IIS, which is sent in response to each user's pre-established connection-this is the whole "push" process.

For the process of sending information, if, as previously envisaged, each user occupies one thread, it is possible to use a writeclient () call to complete the send. However, although a line threads consumes a relatively small amount of resources for the process, the resource consumption of the server will remain considerable if the number of users is large and the number of threads is maintained. What's more, the vast majority of these threads will be dormant, doing nothing, and consuming server resources in an empty state. Thus, there is a better way to use threads instead of simply getting each thread to wait.

In this case, the best solution is to use the thread pool, where the temporarily non-working threads are not caught waiting, but are recycled. The recycled thread enters a thread pool, and when there is a new task, a thread is enabled from the thread pool to complete it, and the thread returns to the thread-pool after the task is completed, and so forth. At the same time, the thread pool should be able to automatically increase or decrease the number of threads based on the load at that time, ideally enough to ensure that its cache threads are not many. Another way to manage the thread pool is to create threads that are equal to the number of CPUs for maximum concurrency without wasting threads. However, this approach does not apply here, where the main thing is to be able to handle all user requests at the same time (although wasting some resources), rather than the most efficient use of the CPU regardless of the resulting unfairness between multiple users. In other words, while multiple users are requesting a service at the same time, although the service to each user will be slow, but still want the implementation to evenly allocate the server's service capacity, instead of processing one after another, if so, some users will wait too long, and the client will probably time out.

It is more complicated to write your own thread pool according to the rules of auto-tuning, but in fact Windows 2000 has built-in support for this thread pool. The benefits of Windows 2000 's thread pool can be fully exploited here. In fact, IIS is also using the thread pool when processing ISAPI programs.

Using the thread pool means that the same session will be handled by a different number of threads, so that the program that processes the session must be thread independent, which also requires IIS support, because the relevant data for IIS is also part of session processing. Fortunately, IIS's ISAPI provides good support for this. ISAPI has the ability to operate asynchronously, and the initial handler, by returning pending, can declare that the session is not ended so that the session enters an asynchronous state and ends the session by invoking the specified interface when session processing is complete. As mentioned earlier, each session of the ISAPI is described by an ECB, which is maintained by IIS, whose address is constant throughout the session, with a pointer that can be accessed at any time, thus guaranteeing thread independence.

Now, for a user who needs to receive information, if his push channel is temporarily unavailable, there must be some mechanism to cache the speech, and when the user's push channel becomes available again, the cached speech is sent out. Otherwise the user will lose the other person's speech to him, network connection interruption is more common, if there is no such mechanism, the problem will become very serious. In addition, because of the limitations of IIS itself, the ISAPI extender can actually have only one asynchronous operation at a time. While the new information may arrive in the process of the asynchronous operation, it is no longer possible to initiate another asynchronous operation, in order to make the speech information at this time not lost, but also need a caching mechanism.

Now, for a user who needs to receive information, if his push channel is temporarily unavailable, there must be some mechanism to cache the speech, and when the user's push channel becomes available again, the cached speech is sent out. Otherwise the user will lose the other person's speech to him, network connection interruption is more common, if there is no such mechanism, the problem will become very serious. In addition, because of the limitations of IIS itself, the ISAPI extender can actually have only one asynchronous operation at a time. While the new information may arrive in the process of the asynchronous operation, it is no longer possible to initiate another asynchronous operation, in order to make the speech information at this time not lost, but also need a caching mechanism.

On the surface, the cache is "per user", that is, each speaker is cached for each specific user. However, from the content of the speech itself to analyze, if a speaker for multiple purposes of the user's push channel is not available, the statement will be on the server side cache multiple copies, which is obviously not cost-effective. Therefore, the caching mechanism is required to ensure that the cache can distinguish between the target users and avoid repeating the same content cache. The end result is a central caching mechanism in which all the entities that speak are stored in the central cache, assigning an ID to each cached content, called the message ID. Then, for each user who is involved in the statement, send a notification that contains the ID, telling it that there is a message arriving, that each user only needs a much smaller local buffer to cache these notifications to solve the problem of accidental interruption of the push channel, thus saving the server resources. When the push channel is re-usable, the corresponding information can be fetched from the central cache and sent to the user, depending on the message ID in the local cache.

The following question is how the central cache should be managed. Since there is only one central cache, access to it is bound to be quite frequent. As a result, it is hoped that access to it requires minimal blocking, requiring minimal synchronization and using a lightweight synchronization mechanism as much as possible. Implementation, the entire cache is divided into a number of buffers, each buffer item caches a message, the size of the buffer is fixed (since the central cache is primarily a cache user speaking, so the buffer size can be determined by limiting the maximum length of the speech). All buffers in the central cache are recycled, that is, when the last buffer is exhausted, the next buffer entry to be used is the first. In a multithreaded environment in a chat room, you can use interlocked functions in order for multiple threads to not select the same buffer item at the same time. The interlocking function is a lightweight synchronization mechanism, and the performance loss caused by the interlock function is tiny compared to other synchronization mechanisms.

One important problem with this mechanism is to avoid overwriting the message by saying that the new message is written to the buffer that contains the message that is still in use. In the actual application, as long as the number of buffer items reasonably set, you can avoid or minimize the coverage. Coverage poses a risk, but the resulting performance gains are even more impressive.

At the same time, the central cache must be able to ensure that the ID assigned to each incoming cache message is not duplicated, otherwise if a user's push channel is not available for a long time, its descriptor will cache a message ID for a longer time, and the central cache may then assign that ID to a new message when the user's push channel is re-usable. The user will receive the wrong information. As can be seen, the message ID cannot simply use the buffer item ordinal.

In the final central cache implementation, the message ID is represented by a 64-bit integer, where a high 32 bits is a read pointer representing a buffer entry index, and a low 32 bit is a sequence number that represents the total number of messages that have been cached on the buffer. In this way, the message on the different buffers must have a different ID, to the same buffer entry at different times of the message, whose ID to repeat after the message content of the buffer is repeated 232 times, then the reference to the message should no longer exist, so in the foreseeable case, the message ID will not be duplicated. The buffer item sequence number can also be obtained using the Interlock function, but since the allocation of the buffer item has been somewhat isolated from concurrency, when the buffer entry is filled out for a long time, there should be no other thread that operates the same buffer while one thread is working on the buffer. As a result, you do not actually need to use an interlock function, simply add a serial number to one.

When extracting information from the central cache, check that the buffer item number recorded in the message ID is the same as the ordinal in the actual buffer, and the notification information is lost if it is consistent to begin extracting the information. Also, the buffer item may be reassigned during the extraction of the information, and another thread will write to the buffer at the same time, and must try to detect the situation, otherwise it will send the wrong information. As long as the increment ordinal before depositing the information, after extracting the information and then checking the serial number, we can guarantee that the extracted information is accurate.

The size of the central cache is a key problem, too large a buffer can cause a waste of resources, too small to cause serious information loss. And in the whole process of the chat room, the cache size required at different times is different. It is therefore necessary to be able to dynamically adjust the size of the buffer so that it adapts to different usage intensities. Unfortunately, this time Windows 2000 does not provide ready-made functionality, and for this purpose an automatic adjustment mechanism is designed to determine the intensity of the buffer usage based on the percentage of information lost per unit of time. If the buffer is too small, there must be a lot of coverage in the unit time, resulting in more information loss, if the loss rate is higher than a threshold, you should increase the buffer; Conversely, if the buffer is too large, the coverage will almost never occur, and if the loss rate is longer than a threshold, the buffer should be reduced. It can be imagined that as long as the reasonable provision of two thresholds, the buffer can be used to achieve optimal utilization.

For the total directory, the chat room descriptor, and the user descriptor, one common feature of synchronization requirements is that it involves "single write, multiple read" Synchronization requirements. This is a typical synchronization requirement and is described in detail in the Jaffrey Richter, "Windows core Programming", which uses the routines in this implementation. However, in the source code of the original routine, a thread that has already acquired a write lock is not supported to obtain a read lock, which can cause a deadlock. I have modified the code to support this situation because it is used when actually writing the code.

With this locking mechanism, it is easy to implement the message ID cache within the user descriptor. The message ID cache requires a mechanism similar to a central cache-cyclic use, auto-overwrite, and guaranteed information integrity. Unlike the central cache, however, the message ID cache is inside the user descriptor, which itself is protected by the descriptor synchronization mechanism, and can be used to simplify its implementation with existing synchronization mechanisms. The simplification of the implementation is mainly embodied in guaranteeing the integrity of the information, the main idea is to use the single-write multi-read synchronization mechanism in turn: the thread that gets the read lock actually writes, and the thread that gets the write lock reads. Since multiple threads can acquire read locks at the same time, using a mechanism similar to the central cache ensures that the ID cache is recycled and the old cache entries are automatically overwritten. When a thread reads an ID, it guarantees that no other thread is writing to the buffer as long as it obtains a write lock, so it does not need a buffer item ordinal like a central cache. From the above analysis, IIS allows only one asynchronous I/O per session, and only needs to read the message if I/O occurs in the session, so there is actually only one thread that needs to read the ID from the ID buffer at the same time, and it is visible that using write locks to read the data does not degrade concurrency performance.

The next problem is that the I/O that sends the message to the user is asynchronous, so how do I drive it to start sending? What will it do after it is sent? When the push channel is just hooked up, it checks to see if there is any information to send, and if so, sends it asynchronously and returns pending. When the asynchronous send completes, the callback function is called, which detects if there are any messages to be sent in the user descriptor that it has just processed, and if so, initiates the next asynchronous send immediately, so that there is no message to send. At this point, the function cannot, of course, go into wait, it should return immediately so that its used thread returns to the thread pool. When the new message arrives, the asynchronous send is stopped and the message cannot be sent, and the thread that writes the message ID is required to initiate the asynchronous send. On the other hand, if the asynchronous send is still in progress, the thread that writes the ID should not start it again, or there will be two asynchronous operations on the same session at the same time, which will result in an error due to the previously mentioned IIS restrictions. Therefore, the thread that writes the ID must be able to detect whether the asynchronous send has been suspended, which can be solved by manipulating a flag variable with an interlocked function. At this point, another foreseeable concurrency problem is that if two threads write to the ID buffer at the same time, it is found that the asynchronous send has been suspended, so at the same time to start the asynchronous send, will also cause two asynchronous operations exist simultaneously, so also need a lock variable to make these write message thread mutually exclusive, to run out A thread to initiate an asynchronous send.

Finally, in the concrete implementation of the process, a very important problem is to lock. You must arrange the appropriate locking strength for each step of each operation, and more rationally arrange the locking order between these steps. Because at the same time, there will always be different kinds of multiple operations in progress, if the lock order and intensity of improper use, light causes performance degradation, heavy caused by deadlocks. The cause of the deadlock is often a logic error at design time, and once a deadlock occurs, the cause will be difficult to detect. Therefore, you must carefully design the code before you write it.

At this point, the server-side design has been largely completed, as to the details of the code-writing issues, as the preface said, not the focus of this article. Related information can be found in the appendix of the UML diagram.

PostScript in the NS browser, a special type of data flow is supported, and its type description field is:
"Content-type:multipart/x-mixed-replace;boundary=boundary", this non-standard data type supports multiple parts of the data, partly divided by boundary, Each section is a snapshot of the same content at different times. With this data type, you might be able to implement it in plain HTML. But only NS support it, IE does not support.

Shortly after I completed the first version of the chat room based on this theory, I found that the capital online (www.263.net) also used similar push technology (which was still in use when this article was written), but it was different in its implementation. Its server is not NT, but the Unix family. It does not use an embedded Web service like the one in the article, it seems to write an additional service, listen to a port other than 80, and then let the user connect to the specified port. The port also executes the HTTP protocol, and if the entire service is self-developed, the server-side implementation of the HTTP protocol should also be written by itself, so that they should not be developed in a small amount. But I don't think it will be more efficient than the implementation of this article.

Similarly, there are differences in the client implementation, and the unfinished page is placed in the UI section, which means that the page that directly receives the push information is visible to the user, so the information it pushes to the page contains many HTML tags, and in fact, it even contains scripts. I don't know what it's good for, but it can be sure that it has a large amount of information to transmit, and because it contains scripts, the script will execute errors if there is an error in the transmission, which is usually more serious than the HTML markup error.

Although the client chat room has been implemented, but unfortunately, for many reasons, I can not deploy it to this article, I wish I could deploy it as soon as possible.

Client chat room construction ideas exclusive sharing

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.