Thinking in Java 16

zhaozj2021-02-11  185

Chapter 15 Network Programming

Historical network programming tends to be difficult, complicated, and very easy to make mistakes.

The programmer must master a lot of details related to the network, sometimes even have a deep understanding of the hardware. In general, we need to understand different "layers" in the networking agreement. And for each connected library, there is generally included a number of functions, which relate to the connection, packaging, and unpacking of the information block; these blocks are transported; and handshake, etc. This is a painful job.

However, the concept of connecting itself is not very difficult. We want to get information on a machine in other places and move them here; or the opposite. This is very similar to the read and write file, but the file exists on the remote machine, and the remote machine has the right to decide how to handle the data we requested or sent.

The best place in Java is its "painless network" concept. The grassroots details about the network have been as appropriate as possible and hidden in the JVM and Java's native installation system. The programming model we use is a model of a file; in fact, a network connection (a "socket") has been encapsulated into the system object, so it can be called like other data streams. In addition, the multi-thread mechanism built by Java is also very convenient when we deal with another network connection.

This chapter will use a series of easy-to-understand examples to explain Java network support.

15.1 Machine logo

Of course, in order to distinguish one machine from elsewhere, and to ensure that it is hoped by it, there must be a mechanism to identify each machine within the network. Early networks only solve how to provide a unique name for the machine in the local network environment. But Java is the entire Internet, which requires a mechanism to identify machines from around the world. In order to achieve this, we use the concept of IP (Internet Address). IP exists in two forms:

(1) Everyone is the most familiar DNS (Domain Service) form. My own domain name is Bruceeckel.com. So assume that I have a computer named OPUS in my own domain, and its domain name can be opus.bruceeckel.com. This is exactly the name used to send an electronic letter to others, and is usually integrated into an online (WWW) address.

(2) In addition, "four" formats can also be used, that is, four sets of numbers separated by the point (.), Such as 202.98.32.111.

Regardless of the case, the IP address is reached internally, a number (notes 1) composed of 32 binary positions (Bit), so each set of IP addresses cannot exceed 255. With Static inetaddress.getbyName () provided by Java.net, we can make a specific Java object express any of the above forms of numbers. The result is an object of type inetaddress, which can be used to form a "socket", and everyone will see this later.

1: This means that you can only get a digital combination of 4 billion or so, people in the world will soon use light. However, according to the current IP addressing scheme currently studying, it will use 128 Bit numbers, so that the uniqueness IP address will not be used in hundreds of years.

As a simple example of using inetaddress.getbyName (), consider assume that you have a dial-up Internet Service Provider (ISP), then what happens. Every time you dial-up, you will be assigned a temporary IP address. But during the connection, the IP address has the same validity as other IP addresses on the Internet. If someone connects your machine according to your IP address, they may use the web or FTP server program that can be run on your machine. Of course, this has a premise, the other party must accurately know the IP you currently allocated. Since IPs obtained each time a dial-up connection is random, how can I accurately master your IP? Below this program uses inetaddress.getbyName () to generate your IP address. In order to run it, you must know the name of your computer in advance. The program only has been tested in Windows 95, but you can enter your own "Start", "Settings", "Control Panel", "Network", and then enter the "Identifier" card. Where "Computer Name" is the content you should enter on the command line.

827 program

For myself, the name of the machine is called "Colossus" (from the same name movie, "giant" means there is a big hard disk on this machine). So once I connect my ISP, I am like the following executive:

Java Whoami colossus

The result is like this (of course, this address may be different):

Colossus / 202.98.41.151

If I tell a friend, he can log in to my personal web server immediately, just specify the target address http://202.98.41.151 (of course, I can't disconnect this at this time). Sometimes, this is a convenient means to send information to others or formally introduced before the Web site.

15.1.1 Servers and Clients

The most basic spirit of the network is to let two machines connect together and "talk" or "communication". Once the two machines have discovered the other party, you can expand a pleasant two-way dialogue. But how do they "find" each other? This is like in the amusement park: a machine has to stay in a place, listening to other machines: "Hey, where are you?"

"Stop in one place" is called "Server"; "Looking for someone" machine is called "client" or "customer". The difference between them is only very obvious when the client tries to connect with the server. Once connected, it has become a two-way communication, who will play a server or a client is not so important.

So the main task of the server is to listen for the request to establish a connection, which is done by the specific server object we created. The client's task is to try to establish a connection with a server, which is done by the specific client object we created. Once the connection is encompassed, then whether the connection is only a magic that has become an IO data stream object whether it is in the server side or the client side. From this point, we can treat it like reading a common file. So once the connection is built, we only need to use the IO command you are familiar with you like Chapter 10. This is the most convenient place for Java network.

1. Test procedure under the premise of no network

Due to a variety of potential reasons, we may not have a client, server, and a network to test your own procedures. We may be practiced in a classroom environment, or write a non-reliable network application, but also get on the network. IP designers have noticed this issue and established a special address - LocalHost - to meet the test requirements in non-network environments. The most common practice to generate this address in Java is:

Inetaddress addr = inetaddress.getbyname (null); if you pass a NULL (empty) value to getByname (), you will use LocalHost. We use inetaddress to index specific machines, and you must get this inetaddress (Internet address) before making further operations. We can't manipulate an inetaddress content (but you can print it out, just like the next example to be demonstrated). The only way to create inetaddress is the Static (static) member method of that class getByname () (this is the most common), getAllByName () or getLocalHost ().

To get the local host address, you can also transfer strings "localhost" directly to it:

Inetaddress.getbyname ("localhost");

Or use its retention IP address (four-point form), like this:

Inetaddress.getbyName ("127.0.0.1");

The results obtained in these three methods are the same.

15.1.2 Port: Unique place in the machine

Sometimes, an IP address is not enough to fully identify a server. This is because multiple servers (procedures) are often run in a physical machine. Each machine expressed by IP also includes "port". When we set up a client or server, we must select a port regardless of the client or the server. Just as we went to meet someone, the IP address is his house, while the port is the room he is.

Note that the port is not a physical presence of a machine, but a software abstraction (mainly for the convenience of expression). The client knows how to connect with it through the IP address of the machine, but how can I act with my own service connection (generally each port is running, one machine may provide a variety of services, such as HTTP And FTP, etc.)? Port numbers play an important role here, which is a second-level address measures. That is, we request a specific port, which is equivalent to the service that is associated with that port number. "News" is a typical example of service. Typically, each service is associated with a unique port number on a particular server machine. The client must know the running port number of the service you ask in advance.

The system service retains the power to use port 1 to port 1024, so you should not let your own designed services occupy these and any other ports known for use. The first example of this book will use port 8080 (for the old 8-bit Intel 8080 chip used by my first machine, it is a machine using a CP / M operating system).

15.2 socket

"Socket" or "socket" is also an abstraction of software for expressing a "terminal" between two machines. For a specific connection, there is a "socket" on each machine, you can imagine there is a virtual "cable" between them. Each end of the cable is inserted into a "socket" or "socket". Of course, physical hardware and cable connections between machines are completely unknown. Abstract basic purpose is to let us do not know how detail as much as possible.

In Java, we created a socket that establishes connections to other machines. The result obtained from the socket is an InputStream and OutputStream (if appropriate converter is used, it is Reader and Writer, respectively, to treat the connection as an IO stream object. There are two data stream-based sockets: ServerSocket, the server uses it "listen" into the connection; and socket, the customer is initially connected. Once the customer (program) is applied to establish a socket connection, ServerSocket returns (by accept () method) a corresponding server-side socket for direct communication. From then on, we have got a real "socket-socket" connection, you can use the same way to treat both ends, because they are the same! At this point, GetInputStream () and getOutputStream () can generate the corresponding inputStream and OutputStream objects from each socket. These data streams must be encapsulated into the buffer. You can format the class according to the method described in Chapter 10, just like any other stream object to be treated. For the naming mechanism of the Java library, the use of ServerSocket is undoubtedly an illustration that is easy to confuse. Everyone may think that Serversocket is best called "ServerConnector", or what is the other name, just do not add a "socket" there. It is also possible that Serversocket and Socket should be inherited from some universal base classes. In fact, these two classes do have several general methods, but it is not qualified to assign them to a general basic class. Instead, the main task of Serversocket is to wait patiently waiting for other machines to connect with it, and then return an actual Socket. This is exactly the name "Serversocket", because its goal is not really a socket, but generates a socket object when others are connected to it.

However, Serversocket does create a physical "server" or socket using the host. This socket will listen to the entered connection, then use the Accept () method to return a "established" socket (local and remote endpoints defined). It is easy to confused that both sockets (listening and established) are associated with the same server socket. The listening socket can only receive a new connection request, and the actual packet cannot be received. So although Serversocket has no significant meaning for programming, it is indeed "physics".

When you create a Serversocket, just give a port number. You don't have to assign an IP address because it is already on the machine represented. However, when you create a socket, you must simultaneously assign an IP address and the port number to which you want to connect (on the other hand, the Socket returned from ServerSocket.Accept () has all this already contains all of this information).

15.2.1 A simple server and client program

This example will operate the server and client in the simplest manner. All of the server is waiting to establish a connection, then create an InputStream and an OutputStream with Socket generated by the connection. After this, it is fed back to OutputStream from INPUTSTREAM, until the line abort (end) is received, and finally closed the connection.

The client is connected to the server and then creates an OutputStream. Text line is sent via OutputStream. The client also creates an InputStream, use it to listen to the server (this example is just the same phrase that feedback back). The server and client (program) use the same port number, and the client uses the local host address to connect to the server (program) in the same machine, so you don't have to complete the test in a physical network (in some configuration environment) Among them, you may need to connect to the true network, otherwise the program cannot work - although it does not actually communicate with that network).

Below is a server program:

831-832 page program

It can be seen that Serversocket is just a port number, does not require IP addresses (because it runs on this machine). When you call accept (), the method will temporarily fall into the pause state (blockage) until a customer tries to establish a connection. In other words, although it is waiting there, other processes can still operate normally (see Chapter 14). After building a connection, accept () will return a socket object, which is the representative of the connection.

The responsibility to clear the socket is artistic. If the ServerSocket builder fails, the program simply exits (note that the ServerSocket builder will not leave any open network socket after failure). In response to this situation, Main () will "throw" out an IOException violation, so it is not necessary to use a TRY block. If the Serversocket builder is executed successfully, all other method calls must be protected into a try-finally code block to ensure that the serverSocket is properly turned off regardless of the block.

The same reason is also applicable to the Socket returned by Accept (). If accept () fails, we must ensure that socket no longer exists or contains any resources so that they do not have to clear them. However, if the execution is successful, the subsequent statement must enter a try-finally block to ensure that the socket can be correctly cleared in the case of their failure. Since the socket uses important non-memory resources, you must be very cautious here, you must clear them yourself ("Destroyer" you must do it yourself to help us do this.

Regardless of the SOCKET generated by Accept (), it is printed into System.out. This means that their Tostring method will get automatic calls. This is produced:

Page 833 Program

Soon, you will see how they work with the customer program.

The next part of the program seems to be just open files in order to read and write, just InputStream and OutputStream are created from the Socket object. Using two "converters" InputStreamReader and OutputStreamWriter, InputStream, and OutputStream objects have been converted to the Reader and Writer objects of Java 1.1, respectively. You can also use Java 1.0 InputStream and OutputStream classes, but the output has a significant advantage to use the Writer mode. This advantage is expressed by PrintWriter. It has an overload builder that can get the second parameter - a Boolean flag pointing to whether it is automatically refreshed when each println () is over, but does not apply PRINT () statement). After the output is written (written into OUT), its buffer must be refreshed, so that the information can be passed out through the network. For this example, refresh is particularly important because customers and servers wait for the arrival of the text content before taking the next step. If the refresh does not occur, the information will not enter the network, unless the buffer is full (overflow), this will bring a lot of problems for this example. When writing a network application, you need to pay special attention to the use of automatic refresh mechanisms. Each time you refresh the buffer, you must create and issue a packet (data seal). As far as the current situation, this is what we hope, because if there is a text line that has not been issued in the package, the server and client "handshake" will stop. In other words, the end of a line is the end of a message. However, in many other cases, the message is not separated by row, so it is better not to use the automatic refresh mechanism, and the built-in buffer decision mechanism decides when to send a packet. In this way, we can make a large packet, and the process can also speed up.

Note that almost all data streams we open, they all have buffering processing. There is an exercise at the end of this chapter, and it is clear that if we don't buffer the data stream, what kind of consequences will be obtained (speed will slow down).

Unlimited WHILE loops read text rows within BufferedReader in, and writes the information to System.out and then writes PrintWriter.out. Note that this can be any data stream, which is only connected to the network on the surface.

After the client issues a row containing "end", the program will abort the loop and turn off the socket.

Below is the source code of the client:

834-835 page program

In Main (), you can see three ways to get the local host IP address: use null, use localhost, or directly use the reserved address 127.0.0.1. Of course, if you want to connect to the same remote host through the network, you can also switch the IP address of that machine. After printing inetaddress addr (by automatic call to the toString () method), the results are as follows:

Localhost / 127.0.0.1

By passing a NULL to getByname (), it will look for localhost default and generate a special retention address 127.0.0.1. Note When you are created in sockets called Socket, INetAddress and port numbers are used. When printing such a Socket object, in order to truly understand its meaning, remember that a unique Internet connection is identified by the following four data, ClientHost, ClientportNumber (client port number), ServerHost (service Host) and ServerPortNumber (service port number). Once the service is started, the port (8080) it assigns it on the local host (127.0.0.1). Once the client issues a request, the machine up and down the machine will be assigned to it (this is 1077), which is also performed on the same machine (127.0.0.1) as the service program. Now, in order to transfer the data to the customer and service programs, each end needs to know where the data is sent. So when the same "known" service program is connected, the customer will issue a "return address" to make the server program know where their data is sent. We can experience this situation in the demonstration output of the server side: socket [addr = 127.0.0.1, port = 1077, localport = 8080]

This means that the server has just accepted the connection of port 1077 from 127.0.0.1, while listening to your local port (8080). On the client:

Socket [addr = localhost / 127.0.0.1, port = 8080, localport = 1077]

This means that the customer has established connections with port 8080 on the 127.0.0.1 machine with its own local port 1077.

Everyone will notice that each number of local ports will increase each time you restart the customer program. This number starts from 1025 (just outside the system reserved 1-1024) and will continue to increase, unless we restart the machine. If the machine is restarted, the port number will start from 1025 (in the UNIX machine, once the reserved sleeve range, the number will start again from the minimum available number).

After creating a Socket object, the process of converting it into buffredreader and PrintWriter is the same as in the server (the same, both in both cases starting from one socket). Here, the customer initializes the communication by issuing a string "howdy" and following a number. Note that the buffer must be refreshed again (this is automatically occurring, by passing to the second parameter of the PrintWriter builder). If the buffer is not refreshed, the entire session (communication) will be suspended because "howdy" used to initialize will never send it (the buffer is not full, not enough to cause the transmission action). Each row returned from the server is written to System.out to verify that everything is operating normally. For the suspension, a "end" needs to be issued. If the client is simply suspended, the server will "throw" an violation.

Everyone can see that we use the same measures to ensure proper clearance from the network resources represented by Socket, which is implemented in a try-finally block.

The socket has established a "dedicated" connection that will continue until it is clearly disconnected (the dedicated connection may be indirectly disconnected, the premise is that a link in one or the intermediate causing a fault crash). This means that both parties involved in the connection are locked in communication, and whether the connection will be continuous in an open state regardless of whether there is data transfer. From the surface, this seems to be a reasonable network. However, it also brought additional overhead for the network. Another way to connect the network will be described later. In that way, the establishment of the connection is only temporary. 15.3 Serving multiple customers

JabberServer works normally, but only provides services for one client program each time. In a typical server, we want to handle the requests for multiple customers at the same time. The key to solving this problem is the multi-threaded tone mechanism. For those languages ​​that do not support multi-threaded, this requirement is undoubtedly difficult. Through the study of Chapter 14, you already know that Java has made as simplified by multi-threaded processing. Since Java's thread processing is very straightforward, let the server control multiple customers are not difficult.

The most basic method is to create a single serverSocket in the server (program), and call accept () to wait for a new connection. Once accept () returns, we get the results obtained Socket, and use it to create a new thread to serve only the specific customer service. Then call accept (), waiting for the next new connection request.

For the server code below, you can find that it is very similar to the JabBerServer.java example, just all operations for providing services for a specific customer have been moved into a stand-alone thread class:

837-839 page program

Each time you have a new customer request to establish a connection, the ServeOnejabber thread acquires the Socket object generated by Accept () in main (). Then, as usual, it creates a bufferedReader and automatically refreshes the PrintWriter object with the socket. Finally, it calls the special method of THREAD start (), which makes it initialized, and then calls Run (). The operations taken here are the same as the previous example: read some things from the sleeve, then feed back it back until it encounters a special "end" end sign.

Similarly, the clearing of the socket must be cautious. In this case, the socket is created outside ServeOnejabber, so clearing work can be "shared". If the ServeOnejabber builder fails, simply simply out of a violation to the caller "throw", then by the caller is responsible for the removal of the thread. However, if the builder is successful, then the ServeOnejabber object must be responsible for the removal of the thread, which is in its run ().

Please note how MultijabberServer is. As before, we create a serversocket and call accept () allows a new connection to build. But this time, the return value of Accept () (a socket) will be passed to the builder for the ServeOnejabber, which is created by it creates a new thread and controls that connection. After the connection is interrupted, the thread can simply disappear.

If Serversocket creates a failed, the violation is thrown again through main (). If successful, the Try-FinalLy code block located outside the outer layer can guarantee the correct clearance. The TRY-CATCH block located in the inner layer is only responsible for preventing the failure of the ServeOnejabber builder; if the builder is successful, the ServeOnejabber thread will turn off the corresponding socket.

To confirm that server code does provide services for multi-name customers, the following program will create many customers (using threads) and establish a connection with the same server. The "time" of each thread is limited. Once expired, leave a space to create a new thread. The maximum number of threads that allowed to create is determined by Final Int Maxthreads. Everyone will note that this value is critical, because if it is set, the thread is likely to exhaust resources and generate unpredictable program errors. 840-842 page program

The JabberClientThread builder gets an inetaddress and uses it to open a socket. You may have seen such a set: Socket is definitely used to create some Reader and / or Writer (or InputStream and / or OutputStream) object, which is the only way to use Socket (of course, we can consider writing one, two Category, which automatically completes these operations, avoiding a large number of duplicate code writing works). Similarly, start () performs initialization of threads and calls Run (). Here, the message is sent to the server, and the information from the server appears back on the screen. However, the "existence time" of the thread is limited, and it will eventually end. Note After the socket is created, but before the builder is completed, if the builder fails, the socket will be cleared. Otherwise, the responsibility of calling close () to the socket is set to the head of the Run () method.

ThreadCount traces the quantity of the currently existing JabberclientThread object. It will be added as part of the builder and impairment at Run () exits (Run () exits means the thread abort). In MultijabberClient.main (), you can see the number of threads will be checked. If there is too much, you will not create excessive temporary. The method then enters the "sleep" state. In this way, once partial threads are last stopped, those threads can be created. Everyone can experiment with gradually increase Max_threads, see how many threads (connectivity) that makes your system resources to the risk of hazards.

15.4 Data News

Everyone I have seen so far is used by the Transmission Control Protocol (TCP), also known as "data stream-based socket". According to the design tenet of the protocol, it has a high degree of reliability and ensures that the data is successfully arrived in destination. In other words, it allows retransmissions that "disappear" due to various reasons. And the order of receiving bytes is the same as they sent. Of course, this control and reliability requires us to pay some consideration: TCP has very high overhead.

There is another agreement called "User Data News Agreement" (UDP), which does not deliberately pursue packets that will be fully transmitted, and cannot guarantee that their arrival will be sent to them. We think this is a "unreliable agreement" (TCP is of course "reliable agreement"). It seems that it seems very bad, but because it is much more, it is often useful. For some applications, such as the transmission of sound signals, if a small amount of packet is lost on the half road, then it is not much more concerned, because the speed of transmission is more important. Most Internet games, such as DIABLO, is also a UDP protocol communication, because the fast slowness of network communications is a smooth decisive factor in the game. You can also think about a newspaper server, if a message is lost, then it really doesn't have to be too nervous. In addition, some applications may pass back a UDP message to the server so that it can be recovered later. If there is no response in the appropriate time, the message will be lost. Java's support for datagrams is substantially the same as it supports TCP sockets, but there is also a significant difference. For the darth, we can place a DataGramsocket in the customer and server program, but unlike Serversocket, the former will not wait for the establishment of a connection. This is because there is no longer "connection", and it is replaced by a datagram. Another essential difference is to the TCP socket, once we built a connection, no longer need to care about who "speak" - just pass the data back to the data. But for the datagram, its packet must know where you come, and where is going to go. This means that we must know this information for each data package, otherwise information will not be transmitted normally.

DataGramsocket is used to send and receive packets, while DataGrampacket contains specific information. When you are ready to receive a datagram, just provide a buffer to place the received data. When the packet arrives, the Internet address and port numbers as the origin of the information are automatically obtained by DataGramsocket. So a DataGrampacket builder for receiving a dataginary is:

DataGrampacket (buf, buf.length)

Among them, BUF is an array of bytes. Since BUF is an array, everyone may weird why constructors cannot investigate the length of the array? In fact, I also have the same feeling. The only reason that can guess is the programming of the C style, and the array there can not tell us how much it is.

You can reuse the receiving code of the datagram, do not have to build a new one each time. When using it (regenerated), the data in the buffer will be overwritten.

The maximum capacity of the buffer is limited only to the allowed data packet size, which is located slightly smaller than 64KB. But in many applications, we would rather become small, especially when sending data. The specific selection of data packets depends on the specific requirements of the application.

When issuing a datagram, DataGrampacket not only needs to include formal data, but also contains the Internet address and port number to determine its destination. So the builder used to output a DataGrampacket is:

DataGrampacket (buf, length, inetaddress, port)

This time, BUF (one byte array) already contains the data we want. Length can be the length of BUF, but it can also be shorter, meaning that we just want to make so many bytes. The other two parameters represent the Internet address to which the packet is to arrive, and a target port (annotation 2) of the target machine.

2: We think TCP and UDP ports are independent of each other. That is, you can run a TCP and UDP service program at the same time at the port 8080, and there is no conflict between the two. Everyone may think that two builders have created two different objects: one for receiving datagrams and the other for sending them. If it is a good object-oriented design, it is recommended to create them into two different classes, rather than having different behaviors (specific behavior depends on how we build objects). This may become a serious problem, but fortunately, DataGrampacket is quite simple, we don't need to be entangled on this issue. This will have a clear explanation in the following example. This example is similar to the MultijabBerServer and MultijabberClient examples for TCP sockets. Multiple customers will report the data to the server, and the latter will feed back it back to the same customer who originally issued a message.

To simplify the job created from a String (or create string from DataGrampacket), this example first uses a tool class called DGRAM:

844-845 page program

The first method of DGRAM uses a string, an inetaddress, and a port number as its own parameters, copy the contents of the String to a byte buffer, and then pass the buffer into the DataGrampacket builder to build a DataGrampacket. Pay attention to " 1" when the buffer is allocated - this is very important to prevent the tailpage. String's getByte () method belongs to a special operation that copies a string into one byte buffer. The method is now "opposed"; Java 1.1 has a "better" approach to do this, but here is regarded as a comment shielded because it cuts part of the String. So even if we compile the program in Java 1.1, you will get a "opposition" message, but its behavior is still correct (this error should be corrected when you read it).

The DGRam.Tostring () method shows the method of Java 1.0 and the method of Java 1.1 (both are different because there is a new type of String builder).

Below is a server code for the datagram demonstration:

845-846 page program

ChatterServer creates a DataGramsocket for receiving messages, not new one when we are ready to receive a new message. This single DataGramsocket can be reused. It has a port number because it belongs to the server, and the customer must know which address will be issued to the data. Although there is a port number, there is no access to the Internet address because it resides in the "this" machine, so I know what my own Internet address is (currently the default localhost). In an infinite while loop, the socket is notified to receive data (Receive ()). Then you temporarily hang until a data report appears, feed it back to our hopes. Receive people - DatagRampacket DP - inside. Packets (Packet) will be converted into a string while inserting the origin of the packet of the packet and socket. This information will be displayed, then add an extra string, pointing out that you have returned from the server.

Everyone may feel a bit confused. As everyone will see, many different Internet addresses and port numbers may be the origin of the message - in other words, the client may reside in any machine (in this demonstration, they all reside In Localhost, the port numbers used by each customer are different). In order to send a message back to its true originating customer, you need to know the customer's Internet address and port number. Fortunately, all of this information has been installed in the DataGrampacket that issued a message, so all things we have to do is taken out with getaddress () and getport (). With these materials, you can build DataGrampacket Echo - it is sent back by the same socket with the received. In addition, once the socket issues a datagram, the Internet address and port information of the "This" machine will be added, so when the customer receives the message, it can take advantage of getaddress () and getport () to know where . In fact, GetDress () and getport () unique cannot tell us where the datagram comes from: we create a datagram to be sent and called getaddress () and getPort () before formally issuing. When the data is officially sent, the address and port of this machine will be written to the datagram. So we got an important principle of using data report: No need to track a source of news! Because it is definitely saved in the datagram. In fact, the most reliable approach to the program is that we don't try to track, but all from the target datagram to extract address and port information (like it is as it is.). To the test server is operating normally, the following will create a large number of customers (threads), they all send the data to the server and wait for the server to feed back them.

Page 847-849

ChatterClient is created into thread, so you can use multiple customers to "harass" servers. It can be seen from it that the DataGrampacket for receiving and the one used for ChatterServer is similar. In the builder, there is no argument (self-variable) when creating a DataGraMpacket, as it does not need to explicitly indicate which specific number of ports. The Internet address for this socket will be "this machine" (such as localhost), and automatically assigns port numbers, which can be seen from the output. As with the server used, this DataGrampacket will be used to send and receive.

Hostaddress is the Internet address of the machine we want to communicate with it. In the program, if you need to create a DataGrampacket ready to pass, you must know an accurate Internet address and port number. It is certain that the host must be on a known address and port number to enable customers to start "sessions" with the host.

Each thread has its own unique identification (although the port number that automatically assigned to the thread is also a unique identifier). In Run (), we created a String message, which contains the identification number of the thread and the message number that the thread is ready to send. We create a datagram with this string to send the specified address on the host; the port number is obtained directly from a constant in the ChatterServer. Once the message is issued, Receive () will be temporarily "blocked" until the server replies this message. All the information attached with the message allows us to know that it returns to this particular thread is delivered from the originating message. In this example, although it is a "unreliable" protocol, it is still able to check if the datagon has been to the place where they go (this is established in the localhost and LAN environment, but in non-local connections There may be some errors). When you run the program, everyone will find that each thread will end. This means that each data packet sent to the server will turn, and feed back the correct recipient. If not this, one or more threads will hang and enter the "clogging" state until their input is revealed.

Everyone may think that the only correct way to pass the document from one machine to another is through the TCP socket because they are "reliable". However, due to the speed of the datagram, it is a better choice. We only need to split the file into multiple datics and numbered each package. The receiving machine will acquire these packets and re-"assemble them; a" header package "will tell the machine how many packages should be received, and other important information required to assemble. If a package is "walking" halfway, the receiving machine will return a datagram and tell the sender retransmission.

15.5 a web application

Now let's think about how to create an app, which is run in a real web environment, which will make Java's advantage to the fullest. Part of this application is a Java program running on the web server, and the other is a "SMD" or "Applet), downloaded from the server to the browser (ie" Customer "). This sector collects information from the user and transfer it to the application running on the web server. The task of the program is very simple: the block will ask the user's E-mail address and pass the E-Mail after verifying this address (not including space, and there is a @ symbol) to send the e-mail to the web server. The program running on the server captures the backup data and checks a data file containing all E-mail addresses. If that address is included in the file, then feedback a message to the browser indicating this situation. This message is displayed by the block. If it is a new address, set it to the list and notify the program piece successfully added an electronic letter address.

If you use a traditional way to solve this problem, we have to create an HTML page that contains text fields and a "Submit" button. Users can type anything you like in the text field and submit it to the server without obstruction (no check at the client). While submitting data, the web page will also tell the server to address the data - the "General Gate Gateway Interface" (CGI) program running the server immediately after receiving this data. Such a CGI program is usually written with Perl or C (sometimes C , but requires server support), and must control all possible conditions. It first checks the data and determines whether the correct format is used. If the answer is negative, the CGI program must create an HTML page that describes the problems encountered. This page will be transferred to the server and then feed back the user by the server. After the user sees the error prompt, it must be submitted again until it passes. If the data is correct, the CGI program opens the data file, either add the electronic letter address to the file or pointing out that the address is already in the data file. Either case, a proper HTML page must be formatted so that the server returns to the user. As a Java programmer, the above solution method is very awkward. And naturally, we hope that everything is done with Java. First, we will use a Java block to be responsible for the client's data validity check, avoiding data from data between servers and customers, and is wasting time and bandwidth between servers and customers, while mitigating an additional burden on the server at an additional HTML page. Then skip the Perl CGI script and replace it to run a Java application on the server. In fact, we have completely skipped the web server here, just establish a connection between the block to the Java application running on the server.

Just as everyone will experience it soon, although it looks very simple, there is actually some unexpected problems make the situation a little complicated. Write the program with Java 1.1 is ideal, but in fact, it is often not available. When writing to this book, the browser with Java 1.1 capabilities is still not much, and even such browsers are now very popular now, they still need to take into account those who upgrade slow. So from a secure perspective, the program code is best written in Java 1.0. Based on this premise, we cannot use JAR files to merge (compressed) .class files in the program. So, we should minimize the number of.class files as much as possible to shorten the download time.

Ok, let's talk about the web server I use (it is it when writing this demonstration program). It really supports Java, but is limited to Java 1.0! Therefore, server applications must also be written in Java 1.0.

15.5.1 Server application

Now discuss the server application (program) problem, I am called NameCollecor (name collector). What happens if multiple users try to submit their E-mail addresses? If NameCollector uses TCP / IP socket, you must use the multi-thread mechanism to introduce the multi-thread mechanism to implement concurrent control of multiple customers. But all of these threads are trying to write data to the same file, where all E-mail addresses are saved. This requires us to set up a lock mechanism to ensure that multiple threads will not access that file at the same time. A "signal machine" can help us to achieve the goa, but may have a simpler way.

If we exchange datagrams, you don't have to use multithreaded. Use a single datagram to "listen" all the datagrams that enter. Once you have an entered message, the program will proceed, and the reply data is passed as a data report to the recipient originally issued. If the data is lost is lost, the user will notice that there is no reply data back, so it can be re-submitted. The server application receives a datagram and is interpreted. The electronic letter address must be extracted, and check the data file saved in this unit to see if the address already contains (if not, add it) ). So we have encountered a new problem now. Java 1.0 does not seem to have enough capacity to easily handle files that contain an electronic letter address (Java 1.1 is not required). However, you can solve this problem with C. Therefore, we have the opportunity to learn the easiest way to connect a non-Java program with a Java program. The Runtime object used by the program contains a method called Exec (), which will independently on a standalone program and return a Process object. We can get an OutputStream, which is connected to the standard input of this separate program; and get an InputStream, which is connected to the standard output. All things to do is written in any language, as long as it can get its own input data from standard input, and write the output result to the standard output. If some questions cannot be ease and quickly solve it with Java (or want to use the original code, do not want to rewrite), you can consider using this method. You can also use Java's "Native Method", but that more skills can be used, you can refer to Appendix A.

1. C procedure

This non-Java application is written in C because Java is not suitable for CGI programming; at least time is not satisfactory. Its task is to manage a list of the electronic letter (E-mail) address. Standard input accepts an E-mail address, the program checks the name in the list, and determines whether there is that address. If there is no existence, it will be added and the report is reported successfully. But if the name is already in the list, you need to point out this to avoid repeating. Don't worry that you can't fully understand the meaning of the following code. It is just a demo, tells you how to write a program in other languages ​​and call it from Java. What language is not important here, as long as it is possible to read data from standard input, and can be written to the standard output.

852-853 page program

This program assumes that the C compiler can accept '//' style annotation (many compilers can be used for a C compiler to compile this program). If your compiler is unacceptable, you can easily delete those comments.

The first function in the file checks if we are passed as the second parameter (pointing to a CHAR) whether the name is in the file. Here, we pass the file as a File pointer, which pointing to an open file (file is opened in main ()). Function fseek () traverses in the file; we have moved here to the beginning of the file. FGETS () reads a line of content from the file list and puts it into the buffer LBUF - not exceeding the specified buffer length BSIZE. All of this work is done in a While loop, so each row in the file will be read. Next, find the new row character with strchr () to delete it. Finally, use strCMP () compare our name to the function and the current line in the file. If the consistent content is found, strCMP () will return 0. The function will then exit and return one 1, indicating that the name is already in the file (note that this function will return immediately after the content is found, and the time will not waste the time on the list of remaining contents). If the list is found, there is no found content, then the function returns 0. In Main (), we open the file with fopen (). The first parameter is the file name, the second is the way to open the file; A means "append", "Open" (or "creation", if the file is not there), so that the end of the file is updated. The FOPEN () function returns a File pointer; if it is 0, it means that the open operation fails. At this point, you need to print an error prompt message with perror () and use the exit () to abort the program.

If the file is successfully opened, the program will enter an infinite loop. Calling the Gets (BUF) is taken out from the standard input (remember the standard input to connect to the Java program) and place it into the buffer BUF. The content of the buffer will then be passed to the AlreadyInList () function. If the content is already in the list, Printf () will send that message to the standard output (Java program is monitoring it). Fflush () is used to refresh the output buffer.

If the name is not in the list, use FSeek () to move to the list of the list, and use fprintf () "Print" to the end of the list. Subsequently, use printf () to indicate that the name has been successfully added (also need to refresh standard output), the infinite loop returns, continue to wait for a new name.

Remember that you can never compile this procedure on your own computer, then load the compiled content to the web server, because the machine can be used by different classes of processors and operating systems. For example, my web server is installed is Intel's CPU, but the operating system is Linux, so you must download the source code first, then use the remote command (through telnet) to direct the C compiler of Linux to compile the program on the server side. .

2. Java program

This program first launches the above C program and establishs the necessary connection so that it is "talking". Subsequently, it creates a datagnet, with its "monitor" or "listening" data packed from the block.

Page 854-956

The first definition in NameCollector should be familiar: Selected ports, create a data packet, and then create a handle to a DataGramsocket. The next three definitions are responsible for connecting to the C program: A Process object is a C program returned after the Java program is started, and that Process object generates InputStream and OutputStream, which represent the standard output and standard input of the C program respectively. Like Java IO, they need "packaging", so we finally get a printstream and DataInputStream. All of this program is done in the builder. To start the C procedure, you need to get the current Runtime object. We use it to call EXEC (), and then return to the process object. In the Process object, you can see data streams through a simple call: getOutputStream () and getInputStream (). From this time, all things we need to consider are to pass the data to the data stream Namelist and get the results from AddResult.

As usual, we connect to the same port of DataGramsocket. In an unlimited While loop, the program calls Receive () - Receive () will be in the "block" state unless a data is reported. After the datagram, its content will be extracted into the String RCVD. We first eliminate the spaces of the string (TRIM), and then send it to the C program. As follows:

Namelist.println (rcvd.trim ());

The reason why you can encode like this is because Java's exec () allows us to access any executable modules, as long as it can read from the standard input and write to the standard output. There are other ways to talk to non-Java code, which will be discussed in Appendix A.

Capture the results from the C program is slightly troublesome. We must call read () and provide a buffer to save the results. The return value of the READ () is the number of bytes from the C program. If this value is -1, it means a problem with a place. Otherwise, we convert the ResultBuf into a string and then clear the extra spaces. Subsequently, this string will go to a DataGrampacket like the same time, and transfer to the same address that is issued. Note that the sender's address is also part of the DataGrampacket we receive.

Remember although the C program must be compiled on the web server, the compilation site of the Java program can be arbitrary. This is because any hardware platform and operating system are used, both the selected bytecode is the same. It is Java's "cross-platform" compatibility.

15.5.2 Namesender SMD

As early as possible, the program must be written in Java 1.0 to adapt to the vast majority of browsers. It is also for this reason, the number of classes we have should be as small as possible. So we don't consider using the DGRAM class in front of the previously designed DGRAM, and all maintenance work of the datagram is taken to the code line. In addition, the sector uses a thread to monitor the response information returned by the server, rather than implementing the runnable interface, and uses a separate thread integrated into the program to do this. Of course, doing so unfavorable to the code, but can generate a single class (and a single server request) program:

858-860 page program

The UI (user interface) of the program is very simple. It contains a TestField (text field) so that we typed an electronic letter address; and a Button (button) to send an address to the server. Two Label (labels) are used to report status information to users. So far, everyone can judge that DataGramsocket, inetaddress, buffer, and DataGrampacket are more troublesome in the network connection. Finally, everyone can see the Run () method implements the thread portion, so that the programming can "listen" response information transmitted by the server.

The init () method sets the GUI with the familiar layout tool, then create a DataGramsocket, which will be used for the delivery and reception of the datagram.

The Action () method is only responsible for monitoring whether we press the "Send" button. Remember, we have been restricted to Java 1.0, so you can't use more flexible internal classes. After the button is pressed, the first action taken is to check the thread PL and see if it is null (empty). If not null, it indicates that there is an active thread that is running. When the message is sent first, a new thread will be launched and use it to monitor the response from the server. Therefore, if there is a thread that is running, it means that this is not the user's first sending a message. The PL handle is set to NULL, while the original monitors are aborted (this is the most reasonable approach, because STOP () has been "opposed by Java 1.2", which is explained in the previous chapter).

Whether this button is pressed, the text in I2 will be cleared.

The next group of statements will check if the E-mail name is qualified. The role of the string.indexof () method is to search for the illegal characters. If you find one, report the situation to the user. Note that all of these work is not necessary to involve network communication, so the speed is very fast, and it will not affect the bandwidth and server performance.

After the name check, it will be packaged into a datam, then send it to the host address and port number as in the previous data reported example. The first label will change and indicate that it has been successfully sent. And the text on the button will also change and become "resend". At this time, the thread will start, and the second tag will tell us that the program is waiting for a response from the server.

The Run () method of the thread will receive data using the DataGramsocket contained in Namesender (Receive ()) unless the data from the server is present, resceive () will be temporarily in the "clogging" or "pause" state. The resulting data package will put it in the DataGramPacketDP of the namesender. Data will be extracted from the package and place the second label of Namesender. Subsequently, the execution of the thread will be interrupted, becoming a "dead" thread. If you do not receive a response from the server in a certain period, the user may become impatient, press the button again. This will interrupt the current thread (after the data is issued, it will build a new one). Since the response data is monitored by a thread, the user can still use the UI during monitoring.

1. Web page

Of course, the program must put it in a web page. The complete web source code is listed below; you can see it slightly. I can use it to automatically collect the name from the Mailing List.

The use of the programmark () is very simple, and the one displayed in Chapter 13 does not differ.

15.5.3 Question

It seems to be a perfect way to take in front. There is no CGI programming, so there is no delay when the server starts a CGI program. Data report seems to have a very fast response. In addition, once Java 1.1 gets most of the adoption of most people, the part of the server can be written in Java (although it is also very easy to use standard input and output the same non-Java program connection). But you must notice some problems. One of them is particularly easy to ignore: Since the Java application is running continuously on the server, most of the time is waiting for the DataGram.Receive () method, so that the CPU has additional overhead. At least, I found this problem on my own server. On the other hand, there will be more more things on that server. And if we use a more heavy server, start the program to solve the problem with "Nice" (a UNIX program, to prevent process greed for CPU resources) or other equivalence programs. In many cases, it is necessary to pay attention to some applications like this - a plug-in Receive () is entirely possible to cause the CPU's paralysis.

The second problem involves a firewall. The firewall can be understood into a wall between its local network and the Internet (actually a dedicated machine or firewall software). It monitors all communication entering and exporting to the Internet to ensure that these communications do not violate the preset rules.

How many conservative firewalls seem to be strictly complied with all rules. If there is no obeying, they will ruthlessly reject them. For example, suppose we are located in a network behind the firewall, start using the web browser with the Internet, the firewall requires all transmissions to connect with the server with the acceptable HTTP port, this port is 80. Now, this Java SMD Namesender is now, it is attempted to pass a data to port 8080, which is set to the "protected" port range 0-1024. The firewall naturally imagines its worst case - some people use viruses or illegal scan ports - do not allow continuation of transmission at all.

As long as our customers build, this type of firewall problem will not occur with the original connection with the Internet (such as through a typical ISP connection Internet). But maybe some important customers are hidden after the firewall, they can't use our designs.

After learning so many things about Java, this is a very depressed thing, because it seems to be given up on the server to use Java, change to learn how to write C or Perl scripts. But please don't despair.

An outstanding scheme is made by Sun Company. If all according to the plan, the web server is ultimately equipped with "Small Sergers" or "Servlet). They are responsible for receiving requests from customers (through the 80-port allowed by the firewall). And no longer starting a CGI program, which will start the small service program. According to Sun's envision, these small servers are written in Java and can only be run on the server. Servers running this applet will automatically initiate them, which make it processed to the client's request. This means that all of our programs can be written in Java (100% pure coffee). This is obviously a very attractive idea: Once Java is used to Java, you don't have to use other languages ​​to handle customer requests on the server.

Since the request can only be controlled on the server, the small service program API does not provide a GUI function. This is very suitable for NameCollector.java, which does not require any graphical interface.

When writing this book, java.sun.com has provided a very cheap small service program dedicated server. Sun Encourages other web server developers to join the support for small servers for their server software products.

15.6 Communication between Java and CGI

The Java program can send a CGI request to a server, which is no different from the HTML table. And like the HTML page, this request can be set to GET (download), or POST (upload). In addition, the Java program can block the output of the CGI program, so it is not necessary to rely on a program to format a new page, and it is not necessary to force the user to rotate from a page to another without an error. In fact, the appearance of the program can be done with the previous version. The code is also simple. After all, it is not very difficult to use CGI to write it (the premise is really understanding it). So in this section, we are ready to run a CGI programming speed class. To address conventional issues, some CGI tools will be created with C so that we can write a CGI program that can solve all problems. The benefits of doing this are very strong in graft - the examples that are about to be seen can run on any system that supports CGI, and there is no problem with firewalls.

This example also illustrates how to establish a connection between the program (Applet) and the CGI program to easily adapten to its own project.

15.6.1 Coding of CGI data

In this release, we will collect names and electronic portions and save them to files in the following form:

First Last ;

This is a very convenient format for any E-mail program. Since only two fields are collected, and the CGI is a special format, there is no easy way here. If you do your own HTML page, you can correctly understand this correctly:

865 page

The above code creates two data input fields (districts), named Name and Email. There is also a Submit button to collect data and send it to the CGI program. Listmgr2.exe is an executable file residing in a special program directory. On our web server, this directory is generally called "cgi-bin" (comment 3). If you can't find the program in that directory, the result will not appear. Fill this form, then press the Submit button, you can see the content below in the browser's URL address window:

http://www.myhome.com/cgi-bin/listmgr2.exe?name=first LAST &SMAIL =MAIL@domain.com&subsmit=submit

3: Under the Windows32 platform, you can test with Microsoft Personal Web Server (Microsoft Personal Web Server) provided by Microsoft Office 97 or other product support. This is the best way to test the test, because it is not necessary to formally connect into the network, you can complete the test in your local environment (speed is also very fast). If you are using a different platform, or if you don't have a product like Office 97 or FrontPage 98, you can find a free web server to test your own test.

Of course, the above URL actually displays no line. It can be seen from which to encode and pass it to CGI. At least one thing can be sure-space is not allowed (because it is usually used to separate the command line parameters). All required spaces are replaced with " ", each field contains a field name (decided by the HTML page), followed by a "=" number and formal field data, and finally ends with a "&".

At this time, everyone may have doubts about " ", "=" and "&". If you must use these characters in the field, what should I declare? For example, we may use the name of "John & Marshasmith", "&" represents "AND". In fact, it encodes the following look: John % 26 Marsha Smith

That is, the special character will be converted into a "%" and follow its hexadecimal ASCII encoding.

Fortunately, Java has a tool to help us carry this coding. This is a static method of the Urlencoder class called Encode (). This method can be tested by the following procedure:

866 page

The program will acquire some command line parameters to combine them into a string composed of multiple words, and the various words are separated by spaces (last space with string.trim ().). They are then encoded and printed.

To call a CGI program, all things to do is to collect data from their own fields or elsewhere, encoding all data into the correct URL style, then assemble it into a single string. After each field name, add a "=" symbol, follow the official data, and then follow one "&". To build a complete CGI command, we placed this string in the URL of the CGI program and a "?". This is a standard method for calling all CGI programs. Everyone will see it immediately that all of these codes and mergers can be easily completed with a program.

15.6.2 SMD

The program is actually simpler than Namesender.java. This part is because it is easy to issue a GET request. Also, you don't have to wait for the reply information. There are now two fields, not one, but everyone will find that many blocks are familiar, please compare Namesender.java.

867-869 page program

The name of the CGI program (so that you can see) is ListMgr2.exe. Many web servers are running on UNIX machines (Linux is getting more favored). According to tradition, they generally do not adopt a .exe extension for their own executables. But in the UNIX operating system, you can call your own program to anything you want. If you are using the .exe extension, the program does not require any modifications to run through UNIX and WIN32.

As usual, the program is set up in your own user interface (this time two input fields, not one). The only significant difference is that it is generated within the action () method. The method of this method is to control the button presses the event. After the name is checked, everyone will find the following code line:

869-870 page program

Name and Email data are extracted in their corresponding text boxes, and the extra spaces in both ends have been removed with TRIM (). In order to enter the list, the email name is forcibly replaced with lowercase forms to accurately compare (prevent error determination based on case-based form). Data from each field encodes the URL form, then assemble the get string as in the HTML page (so, we can use the Java block with any existing CGI programs to meet conventional HTML GET request).

At this time, some Java's magic has begun to play: if you think about any URL connection, just create a URL object and pass the address to the builder. The builder will be responsible for establishing a server connection (for the web server, all connection action is determined based on the strings used as the URL). For this situation, the URL points to the CGI-bin directory of the current Web site (the current Web site is set to getDocumentBase ()). Once the web server sees a "cgi-bin" in the URL, it will then want to follow the name of a program in the CGI-bin directory, which is the target program we have to run. The program name is behind a question mark and a CGI program that will find a parameter string (immediately learned) in the query_string environment variable. After we issue any form of request, we generally get a response HTML page. But if you use Java's URL object, we can intercept anything back from the CGI program, just get an InputStream (input data stream) from the URL object. This is implemented with an OpenStream () method of the URL object, it is to be encapsulated into a DataInputStream. You can then read the data line, if readline () returns a NULL (null value), it indicates that the CGI program has ended its output.

The CGI program we will see is just a line, which is a string for the success of the flag (and the specific reason for failure). This line will be captured and placed in the second Label field, and the user saw something happened.

1. Display a web page from the program

The program can also display the results of the CGI program as a web page, just like they run in ordinary HTML mode. This can be done with the following code:

GetAppletContext (). showdocument (u);

Where u represents the URL object. This is a simple example of realling us to another web page. That page is a CGI program output, but it can be very convenient to enter an original HTML page, so you can build this block, which makes it generated a gateway protected by password, and enters the special part of its Web site:

871-872 page program

The biggest feature of the URL class is to effectively protect our security. You can establish a connection with the web server, you need to know anything behind the scenes.

15.6.3 CGI program written with C

After learning, everyone should be able to write the CGI program with ANSI C with ANSI C. The reason why ANSI C is selected because it is almost everywhere, it is the most popular C language standard. Of course, the current C is also very popular, especially those in the form of GNU C compiler (G ) (annotation 4). Download G for free from all parts of the Internet, and you can use almost all platforms (usually provided with Linux, which are pre-installed). As everyone is about to see, many benefits of object-oriented programming can be obtained from the CGI program.

4: The full name of the GNU is "GNU's Not Unix". This is the first project, which is responsible for developing the "Free Software Foundation" (FSF), dedicated to replacing the original UNIX operating system with a free version. The current Linux seems to be doing things that they have not done. But GNU tools play a vital role in Linux development. In fact, Linux's complete set of software packs has a large number of GNU components.

In order to avoid the first time, there is too many new concepts, this program is not intended to be a "pure" C program; some code is written in normal C - although some of C can be used. But this is not a prominent problem, because the biggest advantage of making C C is to create classes. When parsing CGI information, because we are most concerned about the "Name / Value" pair of fields, you have to use a class (pair) to represent a single name / value pair; another class (CGI_Vector) automatically The parse to which it will accommodate (as a vector) so that you can take each pair (right) when you have time. This procedure is also very interesting because it demonstrates many advantages and disadvantages of C compared to Java. Everyone will see some things; such as Class Keywords. Access control uses the identical keyword public and private, but the usage is different. They control a block, not a single method or field (that is, if the private:, each definition has a private property until we specify public: so). In addition, when creating a class, all definitions automatically think that private.

One reason for using C here is to use the convenience provided by C "Standard Template" (STL). At least, STL contains a VECTOR class. This is a C template that can be configured during compilation, which allows it to accommodate a specific type of object (here is a Pair object). Unlike Java's VECTOR, if we try to place anything other than the Pair object into VECTOR, C VECTOR template causes a compile period error; and Java's Vector is fully charged. And when you take something from the vector, it will automatically become a pair object, no need to perform shape processing. So check in the compile period, which makes the program more "robust". In addition, the running speed of the program can also speed up because there is no need to perform a model during operation. Vector also overloads Operator [], so you can extract the Pair object with very convenient syntax. The Vector template will be used when cgi_vector is created; at that time, everyone can experience such a short definition actually contains such huge energy.

If you refer to the disadvantage, you must do not forget the complexity of PAIR when defined in the following code. Compared to our Java code, PAIR's method defines much more. This is because C programmers must know how to use a copy builder in advance to control the replication process, and to complete the assignment with overloaded Operator =. As explained in Chapter 12, we sometimes have to consider the same thing in Java. But in C , you can't relax these questions almost every moment.

This project first creates a portion that can be reused, composed of Pair and CGI_Vector in the C header file. From a technical point of view, it does not to put these things into a header file. However, as far as the current example, this will not cause any damage, and more Java style, so everyone should look for some when you read the comprehension code:

873-877 page program

After the #include statement, you can see that there is a line:

Using namespace std;

"Namespace" in C solves a problem with Java's Package: hide the library name. The STD namespace reference is a standard C library, and the vector is in this library, so this line is required. The pair class surface is very simple, but two (private) character pointers are accommodated - one for the name and the other for values. The default builder simply sets the two pointers to zero. This is because in C , the memory's memory is not automatically zero. The second builder call method decodeURLString (), generates a decoded string in the newly allocated stack. This memory area must be managed and cleared by the object, which is the same as those seen in "Destroyer". Name () and value () methods generate read-only pointers for related fields. Using the EMPTY () method, we query whether the Pair object is empty; the result is the result of a BOOL-C built-in basic Boolean data type. Operator bool () uses a special form of C "operator overload". It allows us to control automatic type conversion. If there is a PAIR object called P, and in a expression that is the expression of Boolean results, such as if (p) {// ..., the compiler can distinguish it has a pair, and needs It is a Boolean value, so it automatically calls Operator Bool () to perform the necessary conversions.

The next three methods belong to the regular encoding, and they must be used when creating classes in C . According to the so-called "classic form" adopted by the C class, we must define the necessary "original" builder, as well as a copy builder and assignment operator --operator = (and destroyer, used to clear memory). It is necessary to make such a definition because the compiler will "silently". When the object is incorporated, when a function is transmitted, you need to call a copy builder; and when assigning an object, you need to call the assignment operator. Only the working principle of the copy builder and the assignment operator can only write a truly "robust" class in C , but this requires a difficult process (notes 5).

5: My "Thinking In C " (Prentice-Hall, 1995) has used a whole chapter to discuss this topic. If you need more help, please see the chapter.

The copy builder Pair (Const Pair &) will be automatically called as long as an object is incorporated or out of the function. That is, for the object that is ready for making a complete copy, we are not prepared to deliver its address in the function framework. This is not an option provided by Java. Since we can only pass the handle, there is no so-called copy builder in Java (if you want to make a local copy, you can "clone" that object - use clone (), see section 12 chapter). Similarly, if a handle is assigned in Java, it will be simply replicated. But the assignment in C means that the entire object will be copied. In the Copy Builder, we create new storage and copy raw data. But for assignment operators, we must release old memory space before allocating new storage spaces. We have to see may be the most complicated situation of C classes, but that is the supporters of Java to demonstrate more powerful evidence that Java is much easier than C . In Java, we can freely deliver your handle, and after work is responsible by the garbage collector, you can easily.

But things did not finish. The PAIR class is NM and VAL use char *, the most complex situation is mainly surrounded by the pointer. If you use a more trendy C String class instead of char *, things will become more simple (of course, not all compilers provide support for String). So, the first part of Pair looks like this: 878 Page

(In addition, this class decodeurlstring () will return a string instead of a char *). We don't have to define a copy builder, operator = or destroyer because the compiler has helped us, and it is very good. But even if some things are automated, C programmers must also understand the details of copy build and assignment.

The remaining part of the PAIR class consists of two methods: decodeurlString () and a "help" method translateHex () - will be used by decodeurlstring (). Note that translateHex () does not prevent user malicious input, such as "% 1h". After allocating enough storage space (must be released by the destroyer), decodeURLString () will be traversed, put all " " to a space; put all the hexadecimal code (with a "%" head) Corresponding characters.

CGI_Vector is used to resolve and accommodate the entire CGI GET command. It is inherited from the STL Vector, and the latter is exemplified as accommodating pair. Inheritance in C is expressed in a colon, and in Java, use Extends. In addition, the inheritance is default that the private property, so it is almost certainly necessary to use the public keyword, just like this. Everyone will also find that CGI_Vector has a copy builder and an operator =, but they are declared into private. This is to prevent the compiler from synchronizing two functions (if you don't declare them yourself, both will synchronize). But this also prohibits customer programmers to press values ​​or pass a CGI_Vector by assigning values.

The CGI_Vector's job is to get query_string and parse it into "Name / Value", which needs to be completed with the help of PAIR. It first copies the string to the local allocated memory and uses the constant pointer START to track the starting address (it will be used to release memory in the destroyer later). Subsequently, it uses its own nextpair () method to resolve the string into the original "Name / Value" pair, and each pair is separated by a "=" and "&" symbols. These pairs are passed to the PaIR builder by nextpair (), so NEXTPAIR () returns a PAIR object. The object is then joined to the Vector with PUSH_BACK (). Nextpair () will return a zero value after traversing the full query_string.

Now the basic tools are defined, they can be used simply in a CGI program, just like this:

Page 879-881

The AlreadyInList () function is almost identical to the previous version, but it assumes that all electronic letters are in one "<>".

When using the GET method (by the internal settings in the form boot command, this is controlled here by data transmission), the web server collects all information behind "?" And puts them into environment variable Query_String (Query strings). So in order to read those information, you must get the value of query_string, which is done with the standard C library function Getnv (). In Main (), pay attention to how easy to pars Query_String: Just pass it to builder (name query) used for CGI_Vector objects, all remaining work will be made. From this point, we can take the name and value from Query, treat them as an array (this is because Operator [] has been overloaded in Vector). In debug code, you can see how this is working; debug code package is between the pre-processor boot command #if defined (debug) and #ENDIF (Debug). Now we urgently need to master some things related to CGI. The CGI program passes their input with one of two ways: passed through Query_String during GET execution (currently used), or through standard input during POST. However, the CGI program sends its own output through standard output, which is usually implemented with the printf () command of the C program. So where is this output? It returns to the web server and determines how to handle it by the server. The basis of the server decision is the content-type header data. This means that if the Content-Type head is not the first thing it see, I don't know how to handle the received data. Therefore, we have to make all CGI programs from the Content-Type header from the Content-Type header.

In this case, we hope that the server will directly feed back all the information directly back to the client (that is, our block, they are waiting for their reply). The information should be originally unable, so Content-Type is set to Text / Plain (plain text). Once the server sees this head, you will send all the strings directly to the customer. So each string (three for error conditions, an addition to success) will return to the block.

We add an electronic letter name (the user's name) with the same code. However, in the case of the CGI script, there is no infinite loop - the program is just simply responsive, and then it is interrupted. When there is a CGI request arrival each time, the program will start, and the request will be reacted and then shut down it. Therefore, the CPU cannot fall into an empty waiting, and only the programs and the hidden dangers are available when opening the file. When the Web server is controlled to the CGI request, its overhead will reduce this hidden danger to the lowest level.

Another benefit of this design is because pair and cgi_vector have been defined, most of the work is sent to us automatically, so you only need to modify main () to easily create your own CGI program. Although the small service program will eventually become more popular, C is still very convenient in order to create a fast CGI program.

15.6.4 Concept of POST

There is no problem in using GET in many applications. However, the GET requires that its data passes its own data to the CGI program through an environment variable. But if the get string is too long, some web servers may use light their own environmental space (if the string length exceeds 200 characters, you should start concerning this issue). CGI provides a solution for this: POST. With POST, the data can be encoded and connected to the same method as get. But POST uses standard input to pass the encoded query string to the CGI program. All what we have to do is to determine the length of the query string, and this length is saved in the environment variable content_length. Once you know the length, you can freely allocate the storage space and read the specified number of characters from the standard input. For a CGI program used to control the POST, the PAIR and CGI_Vector provided by cgitools.h can be used without change. The following program reveals how easy it is to write such a CGI program. This example will use "pure" C , so the Studio.H library is replaced by Iostream (IO data stream). For iSostream, we can use two pre-defined objects: CIN, used to input to standard input; and COUT for connection with standard output. There are several ways to read data from the CIN and write it to cout. However, the following program is prepared to adopt standard methods: use "<<" to send information to cout, and read data from the CIN with a member function (this is READ ()):

883 page

The getenv () function returns a pointer to a string, and that string indicates the length of the content. If the pointer is zero, it indicates that the Content_length environment variable has not yet been set, so there is certain place to have a problem. Otherwise, you must use the ANSI C library function ATOI () to convert the string into an integer. This length will be used with New, allocate enough storage space to accommodate queries (additional airbeats). READ () is then called for CIN (). The read () function needs to get a pointer to the target buffer and the number of bytes to be read. The Query_Str is then suspended with empty characters (NULL), indicating that the end of the string has been arrived, which is called "empty".

At this time, the query string we get is not distinguished from the Get query string, so it passes it to the builder used for CGI_Vector. Like the previous example, we can free VECTOR in different fields.

To test this program, it must be compiled into the CGI-BIN directory of the host web server. Then you can write a simple HTML page for testing, just like this:

884 program

After filling this form and submits it, you will get a simple text page, which contains the results of the paid. You can know if the CGI program is working properly.

Of course, use a block to submit data more interesting. However, the commit submission belongs to a different process. After the CGI program is called in a conventional manner, a connection to the server must be established separately to feed the query string to it. The server then processes, and then feeds the query string back to the CGI program by standard input.

To establish a direct connection to the server, you must obtain the URL you created yourself and then call OpenConnection () Create a URLConnection. However, since UrlConnection generally does not allow us to send data to it, you must call the setDoOutput (TRUE) function, while the call also includes setDoinput (true), and setallowuserAction (false) - annotation 6. Finally, getOutputStream () can be called to create an OutputStream and package it into a DataOutputStream so that you can communicate with the traditional way. The following is a block used to complete the above-described work, and must execute it after collected data from each field: 885-887 page

6: I have to say that I haven't really understood what happened here. These concepts are coming from the "Java NetWork Programming" built by Elliotte Rusty Harold, which is published by O'Reilly in 1997. He mentioned many of the fascinating bugs that appeared in the Java network function library. So once these areas are involved, things are not writing code, then let it run as simple as it. Be sure to be alert to potential traps!

After the information is sent to the server, we call getInputStream () and package the return value into a DataInputStream so you can read the results. One thing to note is that the result is displayed in a textarea (text area) in the form of text line. Why not simply use getAppletContext (). ShowDocument (u)? In fact, this is one of those traps. The above code can work well, but if you try to switch ShowDocument (), almost everything will stop running. That is, showdocument () is indeed running, but the return result obtained from PostTest is "Zero Content_length" (zero content length). So I don't know why, showdocument () blocks the post query from passing to the CGI program. I am hard to judge that this is a bug that will repair in the later version, or because my understanding is not enough (I have been very vague of this book). However, in any case, as long as you can stick to the content returned from the CGI program in the text area, there is no problem when running.

15.7 Connect the database with JDBC

It is estimated that nearly half of software developments should involve customer (machine) / server operations. Java guarantees an excellent ability to build a client / server database application that is unrelated to the platform. In Java 1.1, this guarantee that it is implemented through the Java Database Connection (JDBC).

One of the most important issues of the database is the specifications between the companies. There is indeed a "standard" database language, "Structure Query Language" (SQL-92), but usually must know how to deal with which database company is dealing with, otherwise it is very easy to issue problems, although there is so-called "standard" . JDBC is designed for "unrelated to the platform", so don't care about what you want to use when programming. However, it is still possible to issue a call to certain database companies from JDBC, so it is still uncontrollability.

Like many of the APIs in Java, JDBC has also simplified as much as possible. Our method calls to the data when collecting data from the database: That is the same as the database connection, create a statement and execute the query, then process the result set. To achieve this "unrelated to the platform", JDBC provides us with "Drive Manager" that dynamically maintains all driver objects needed for database queries. Therefore, if you want to connect different types of databases developed by three companies, you need three separate driver objects. The driver object is automatically registered by the Drive Manager when loading and can be loaded with class.Forname ().

To open a database, you must create a "database URL", which specifies the following three aspects:

(1) Point with "JDBC" to point out the JDBC.

(2) "Subject": The name of the driver or the name of a database connection mechanism. Since the JDBC's design absorbs a lot of inspiration from ODBC, the first seed protocol that can be selected is "JDBC-ODBC Bridge", which can be specified with the "ODBC" keyword.

(3) Database identifier: The use of the database driver varies, but generally provides a comparison logical name, the database management software map (corresponding) to a physical directory saved in the data sheet. To make your database identifier, you must use your own database management software to register your favorite name (the specific process of registration changes with the running platform).

All this information is uniformly compiled into a string, that is, "Database URL". For example, if you want to connect the same identified as "people" by the ODBC sub protocol, the corresponding database URL can be set to:

String dburl = "jdbc: odbc: people"

If you pass a network connection, the database URL also needs to include information identified by the remote machine.

After you are ready to connect to the database connection, you can call the static method DriverManager.getConnection (), pass the URL of the database, and the username password required to enter that database to it. The resulting return result is a Connection object that uses it to query and manipulate the database.

The following example will open a contact information database and query a person's last name according to the parameters provided by the command line. It only chooses the names of those with E-mail addresses, and then print out all owners who meet the query criteria:

888-889 page program

It can be seen that the creation process of the database URL is exactly the same as the above. In this example, the database does not have a password protection, so the username and password are an empty string.

After using DriverManager.getConnection (), then you can create a Statement object according to the results Connection object, which is implemented with the CreateStatement () method. According to the result statement, we can call EXECUTEQUERY (), to transfer a string containing the SQL-92 standard SQL statement (soon see how to automatically create such statements, so there is no need to know about SQL more thing).

The executeQuery () method returns a ResultSet object, which is very similar to the heir: the next () method moves the successor to the next record in the statement; if the end of the result set is reached, returns NULL. We will definitely return a RESULTSET object from ExecuteQuery (), even if the result is an empty set (that is, no violation is generated). Note that next time () must be called before trying to read any recorded data. If the result set is empty, then this first call for next () will return false. For each record in the result set, you can use the field name as a string (of course, there are other methods) to select different fields. It is also important to pay attention to the case of the field name is irrelevant - the SQL database does not care about this problem. To determine the type returned, you can call getString (), getfloat (), etc.. At this time, we have already got your database data with Java's original format, and then you can use Java code to do anything you want to do. 15.7.1 Let the example run

For JDBC, the code itself is very easy to understand. The most confusing part is how to make it run on your own specific system. The reason why it will be confused because it requires us to master how to make the JDBC driver correctly, and how to use our database management software to set a database.

Of course, the specific operation process will also be different on different machines. But here is available in the 32-bit Windows environment to effectively help everyone understand the operations on other platforms.

1. Step 1: Look for JDBC drivers

The above program contains the following statement:

Class.Forname ("Sun.jdbc.odbc.jdbcodbcdriver");

This seems to hinder a directory structure, but everyone should not be cheated by it. In my hand in this JDK 1.1 installation version, there is no file called JDBCODBCDRIVER.CLASS at all. So if you look for it after watching this example, you will inevitably return. Another example provided is a false name, such as "MyDriver.className", but people don't help from the literal. In fact, the above-mentioned statement used to load the JDBC-ODBC driver (actually the only driver supplied with JDK 1.1) has many places in the online document (especially in a marked "JDBC-ODBC Bridge Driver". Inside the page). If the loading statement above cannot work, its name may have changed as Java's new version of the new version; at this time, it should be found in the online documentation.

If the load statement is wrong, it will get a violation at this time. In order to verify that the driver load statement is working properly, then the statement is temporarily set to the code between the CATCH clause. If a violation does not appear when the program is run, it indicates that the driver load is correct.

2. Step 2: Configure the database

Similarly, we are limited to work in a 32-bit Windows environment; you may need to study your own operating system to find out the configuration of your platform.

First open the control panel. There may be two icons contain "ODBC" words, and the "32-bit ODBC" must be selected because the other is set to keep backward compatibility with the 16-bit software, and there is no result of the JDBC mix. After double-click the "32-bit ODBC" icon, it should be a card dialog that there is a plurality of card tags above, including "user DSN", "system DSN", "file DSN", and more. Among them, "DSN" represents "Data Source Name". They are related to the JDBC-ODBC bridge, but the only important place "System DSN" is only important when setting up the database. Despite this, you need to set your own database in Document DSN because you need to test your own configuration and create a query. This allows the Microsoft Query tool (provided with Microsoft Office Support) to find the database correctly. Note that some software companies also designed their own query tools. The most interesting database is one of our already used. Standard ODBC supports multiple file formats, including some formats for different companies, such as dbase. However, it also includes a simple "comma-separated ASCII" format, which is almost per data tools that can be generated. For the current example, I only select my own "people" database. This is a database I have been maintaining for many years, using various contact management tools in the middle. I export it into a comma-separated ASCII file (generally having a .csv extension, export communication book with Outlook Express, can also use the same file format). In the "Document DSN" area, I press the "Add" button to select the text driver (Microsoft Text Driver) for controlling a comma to divide the ASCII file, and then undo the selection of "Using Current Directory" to export data files Specify the directory yourself.

Everyone will notice that when these work is doing, there is no actual designation of a file, just a directory. That's because the database is usually made up of a series of files in a directory (although other forms may also be employed). Each file typically contains a single "data table", and the SQL statement can generate the result from multiple tables from the database (this is called "combination", or Join) only contains a single list database ( This is usually called a "flat file database". For most questions, if a simple data storage and the scope can be used, multiple data sheets must be used. Through "union", it is achieved. We call these "relational" databases.

3. Step 3: Test configuration

In order to test the configuration, you need to verify that the database can be "see" by querying it. Of course, the above JDBC exemplary program can be simply run, and the following statement can be added:

Connection C = DriverManager.getConnection

DBURL, User, Password;

If a violation is thrown, it indicates that your configuration is incorrect.

However, it is necessary to use an automated query generation tool. I am using Microsoft Query provided with Microsoft Office, but you can choose one by yourself. The query tool must know where the database is, and Microsoft Query requires me to enter the ODBC Administrator's "file DSN" card and add an entry there. The text driver is also specified, and the directory where the database is saved. Although this entry can be named as anything you like, it is best to use the same name as "System DSN".

After doing these work, use the query tool to create a new query, you will find your database can be used. 4. Step 4: Establish your own SQL query

The query created by Microsoft Query not only points out the target data inventory, but also automatically generates SQL code to insert it into my own Java program. I hope this query can check if there is the same "last name" that is typed in the command line when the Java program is launched during the record. So as a starting point, I search for my surname "eckel". In addition, I hope only those names with corresponding E-mail addresses. The steps to create this query are as follows:

(1) Start a new query and use the Query Wizard. Select the "People" database (equivalent to open the database connection with the adaptable database URL).

(2) Select the "people" table in the database. From this data sheet, select First, Last, and Email columns.

(3) Under "Filter Data" (filter database), select Last, and select "Equals" (equal to), plus parameter Eckel. Click the "AND" single button.

(4) Select Email and select "Is Not Null".

(5) Under "Sort By", select First.

The results of the query will show us whether they can get what they want.

You can now press the SQL button. No need to intervene in any of us, the correct SQL code will come out immediately so that we will paste and copy. For this query, the corresponding SQL code is as follows:

893 page

If the query is more complicated, manual coding is extremely easy to go wrong. But using a query tool, you can interactively test your own query and automatically get the correct code. In fact, it is difficult to coding for these things.

5. Step 5: Modify and paste in your own query

We noticed that the code used in the above code is different from the code used in the program. It is because the query tool is limited to all the names, even if there is only one data table involved (if the multiple data tables involves multiple data tables, this limitation avoids conflicts from the same name data column from different tables). Since this query only needs to use a data table, consider removing the "people" qualifier from most names, just like this:

893 page program

In addition, we don't want "hardcod" programs, so you can only find a specific name. Instead, it should be able to find a name that we provide at the command behavior. So there is also necessary modifications and convert the SQL statement into a dynamically generated string. As follows:

Page 893-894

SQL has another way to insert the name into a query, named "Probes", which is very fast. But for most experimental database operations, as well as some primary applications, it is very good to build a query string with Java.

As can be seen from this example, using the tools currently - especially query build tool - database programming involving SQL and JDBC is very simple and intuitive.

15.7.2 Find the GUI version of the program

The best way is to let the lookup program remains running. When you look for something, just simply switch to it, and type the name you want to find. Below this program will find the program as an "Application / Applet", and add the name to automatically fill in the function, so you don't have to type the full last name, you can see the data:

Page 894-896

Many logic of the database is the same, but you can see that a TextListener added here is used to monitor the input of the TextField (Text Field). So just type a new character, it will try to find "last name" in the database, and display the first record that matches the current input (placed in the Completion Label, and uses it as the text to look for). Therefore, as long as we type enough characters, let the program find a unique record that is consistent with it, you can stop. 15.7.3 How to complicate the JDBC API

When you read JDBC's online help documentation, we tend to have fearfulness. In particular DatabaseMetaData Interface - seen most of the interface and Java contrary, its volume is very large - there are a large number of methods, such as dataDefinitionCausesTransactionCommit (), getMaxColumnNameLength (), getMaxStatementLength (), storesMixedCaseQuotedIdentifiers (), supportsANSI92IntermediateSQL () SupportslimitedouterJoinS (), etc. Do they have any meaning?

As early as possible, the database initially is in a chaotic state. This is mainly due to the requirements of various database applications, so the database tool is very "powerful" - in other words, "huge". Just in recent years have emerged in the general language of SQL (there are many other database languages ​​that are commonly used). But even "standard" like SQL, there are countless variants, so JDBC must provide a huge DatabaseMetadata interface, so that our code can truly use the "standard" SQL database of the currently connected "standard" SQL database. In short, we can write simple and portable SQL. But if you want to optimize the speed of the code, then in order to adapt to the characteristics of different database types, our writing code is big.

Of course, this is not a Java defect. Differences between database products are a reality we have to face with JDBC. However, if you can write a common query, don't have to care too much, then things are much simpler. Even if you have to adjust the performance, just know the final platform, you don't have to write different optimized code for each case.

In the Java 1.1 product released by Sun, a series of electronic documents is provided with a more comprehensive introduction to JDBC. In addition, in the "JDBC Database Access With Java" published in 1997, Addison-Wesley is also provided in many useful information on this topic in "JDBC Database Access With Java" published in 1997. At the same time, some new books on JDBC have often occurred in the bookstore.

15.8 Remote Method

In order to perform code on other machines over the network, traditional methods are not only difficult to learn and master, but also very easy to make mistakes. Thinking this problem is: Some objects are just in another machine, we can send a message to them and get the return result, just like those objects located on their own local machine. It is this abstraction using Java 1.1 "Remote Method Call" (RMI). This section will guide you to experience some necessary steps to create their own RMI objects.

15.8.1 Remote Interface Concept

RMI has strong dependence on the interface. When you need to create a remote object, we hide the implementation details of the grassroots by passing an interface. So when the customer gets a handle of the remote object, they really get the interface handle. This handle is connected to some local root code, which is responsible for communicating through network. But we don't care about these things, just send messages through your own interface handle. When you create a remote interface, you must follow the following rules:

(1) The remote interface must be a public attribute (no "package access"; that is, it can't be "friendly"). Otherwise, once the customer tries to load a remote object that implements a remote interface, it will get an error.

(2) The remote interface must extend the interface java.rmi.Remote.

(3) In addition to violations related to the application itself, each method in the remote interface must declare java.rmi.RemoteException in its own throws clause.

(4) A remote object (whether directly, or in the local object is embedded), it is not possible to declare a remote interface as a parameter or returned value.

Below is a simple remote interface example, which represents a precise timing service:

898 program

It is similar to other interfaces on the surface, just extension of Remote, and all of its methods will "throw" RemoteException (remote violation). Remember that interfaces and all methods are public.

15.8.2 Implementation of the remote interface

The server must contain a class that extends UnicastRemoteObject and implements a remote interface. This class can also contain additional methods, but customers can only use methods in remote interfaces. This is obvious because the customer gets just a handle to the interface, not the class of it.

The builder must be explicitly defined for the remote object, even if only one default builder is defined, use it to call the underlying class builder. It must be explicitly written because it must "throw" RemoteException violation.

The process of implementing remote interface PerfectTime is listed below:

899-900 page program

Here, Main () controls all the details of the setting server. When saving the RMI object, you must take the following operations in some place in the program:

(1) Create and install a security manager to support RMI. As part of the Java distribution package, the only one of RMI is RMISecurityManager.

(2) Creating one or more instances of the remote object. Here, everyone can see that the PerfectTime object is created.

(3) Register at least one remote object to the RMI Remote Object Registry. A remote object has a method to generate a handle that points to another remote object. In this way, the customer only needs to access it once in the registry to get the first remote object.

Set the registry

Here, you can see a call to the static method naming.bind (). However, this call requires the registry to operate as a standalone process on the computer. The name of the registry server is RMIREGISTRY. In the 32-bit Windows environment, you can use:

Start RMIREGISTRY

Let it run in the background. In UNIX, use:

RMIREGISTRY &

Like many network programs, the RMIREGISTRY is located at a machine to start its IP address, but it must also monitor a port. If you call RMIRGISTRY as above, do not use parameters, the port of the registry will default to 1099. If you want it to be in another port, simply add a parameter to the command line, specify the port number. For this example, the port will be in 2005, so RMIREGISTRY should be started below (for 32-bit Windows): Start RMIREGISTRY 2005

For UNIX, use the following command:

RMIREGISTRY 2005 &

The information related to the port must be transmitted to the bind () command, and the IP address of the machine where the registry is located at the same time. But if we want to test the RMI program locally, it will bring problems like this network program in this chapter. In JDK 1.1.1, there is a problem with the following two aspects (notes 7):

(1) LocalHost cannot work with RMI. So in order to complete the test of RMI on a single machine, the name of the machine must be provided. In order to investigate the names of your machine in the 32-bit Windows environment, you can enter the control panel, select "Network", select "Identify" card, where the computer's name is listed. For my own situation, my machine is called "colossus" (because I have a variety of large-capacity hard drives to save a variety of different development systems - Clossus is the meaning of "giant"). It seems that the uppercase form will be ignored.

(2) Unless the computer has an active TCP / IP connection, RMI does not work, even if all components only need to communicate with each other in the local machine. This means that you must connect to your own ISP (Internet service provider) before trying to run the program, otherwise some of the impulse violation messages will be obtained.

7: To find out this information, I don't know how many brain cells have been damaged.

Considering these factors, the bind () command becomes the following look:

Naming.bind ("// Colossus: 2005 / PerfectTime", PT);

If you use the default port 1099, it is not necessary to specify a port, so you can use:

Naming.bind ("// colossus / perfecttime", pt);

In the future version of JDK (after 1.1), once the problem with localhost is all correct, the local test can be performed normally, remove the IP address, only identifier:

Naming.bind ("PerfectTime", PT);

The service name is arbitrary; it is just like PerfectTime, with the class name here, but you can modify according to the situation. The most important thing is to make sure it is a unique name in the registry so that the client will normally get a remote object. If this name is already in the registry, you will get a AlreadyBoundException violation. To prevent this problem, consider sticking to rebind () and abandon bind (). This is because rebind () either adds a new entry, or replace the entry of the same name.

Although main () exits, our object has been created and registered, so it will remain active by the registry, waiting for the customer to reach and issue a request to it. As long as the RMIREGISTRY is running, and we don't call the naming.unbind () method for the name, the object is certainly in that place. Considering this reason, when we design your own code, you need to close RMIREGISTRY and restart it when you compile a new version of the remote object. It is not necessary to start RMIREGISTRY as an external process. If you know that yourself is required to use the only app to register, you can start it inside the program, use the following code:

LocateRegistry.createREGISTRY (2005);

As in front, 2005 represents the port number of our choice in this example. This is equivalent to execute RMIREGISTRY 2005 at the command line. But when designing RMI code, this approach is often more convenient because it cancels the additional steps required to start and abort the registry. Once this code is performed, "Bind" - Bind () can be used as previously before using Naming.

15.8.3 Creating roots and dry

If you compile and run PerfectTime.java, even if RMIREGISTRY is run correctly, it will not work. This is because the frame frame of the RMI has not yet been in place. First, you must create roots and dry to provide network connection operations and enable us to disguise remote objects into a local object within your machine.

All these behind-the-scenes work are quite complex. We have passed from remote objects, and any objects you have passed must "Implement Serializable" (if you want to pass a remote reference, not the entire object, the parameters of the object can "Implement Remote"). Therefore, it is possible to imagine that when the root and dry through the network "Collection" and return the result, sequentialization and data are automatically re-assembled. Fortunately, we don't have to understand any details of these aspects, but roots and dry are must be created. A simple process is as follows: Call the RMIC in the compiled code, which creates some files. So the only thing to do is to add a new step for the compilation process.

However, the RMIC tool has a great correlation with a particular package and class path. PerfectTime.java is located in the package c15.ptime, even if we call the RMIC within the same directory in the same directory in PerfectTime.class, the RMIC cannot find a file. This is because it searches for classpaths. Therefore, we must specify the class path at the same time, just like this:

RMIC c15.ptime.perfecttime

When this command is executed, it is not necessarily to be in a directory containing PerfectTime.class, but the result is placed in the current directory.

If the RMIC is successfully run, there will be more new classes in the directory:

PerfectTime_stub.class

PerfectTime_skel.class

They correspond to root (STUB) and dryness. Now, we are ready to make the server communicate with our customers.

15.8.4 Using Remote Objects

RMI all the purpose is to simplify the use of remote objects as much as possible. The only additional thing we have to do in the customer program is to find and retrieve the remote interface from the server. Since then, the remaining things is ordinary Java programming: send messages to objects. Below is a program using PerfectTime:

903-904 page program

The ID string is the same as the string of the Naming registration object, and the first part indicates the URL and the port number. Since we are ready to use a URL, you can also specify a machine on the Internet.

From Naming.lookup () returned to the remote interface instead of category. If you change the class, you will get a violation prompt. In the following method call:

T. GetPerfectTime ()

We can see the handle of the remote object, which is used by the programming and the programming of the local object is very similar (only one difference: "throwing" a remoteException violation).

15.8.5 RMI alternative

RMI is just a way to create a special object, and the object it creates can be released through the network. Its biggest advantage is to provide a "pure Java" program, but if there are many code written in other languages, RMI may not meet our requirements. At present, the two most competitive alternatives are Microsoft's DCOM (according to Microsoft plans, it will eventually be ported to other platforms other than Windows) and CORBA. Corba starts support from Java 1.1, is a new design concept for cross-platform applications. In "Client / Server Programming with Java and Corba" edited by ORFALI and HARKEY (published in John Wiley & Sons), you can get a comprehensive introduction to distributed objects in Java (this book seems to have some prejudice to CORBA. ). One book gives CORBA to "Java Programming With Corba" written by Andreas Vogel and Keith Duddy, John Wiley & Sons is published in 1997.

15.9 Summary

Due to the limited space limit, there are many other concepts involved in the network, not introduced to everyone. Java also provides considerable support for the URL, including providing protocol controllers for different types of customers on the Internet.

In addition, a technology that is gradually popular is called servlet server. It is an Internet server application that controls customer requests through Java, not the CGI (General Gateway Interface) protocol that is very slow, and quite troublesome. This means that in order to serve the service at the server, we can use Java programming without having to use other languages ​​they are not familiar. Since Java has excellent gravesting capabilities, it is not necessary to care about what platform is to accommodate this server.

All of these and other features have been detailed in the "Java Network Programming" book. The book was published by Elliotte Rusty Harold, O'Reilly published in 1997.

15.10 practice

(1) Compile and run the JabBerServer and JabberClient programs in this chapter. Then edit the program, delete all the buffer mechanisms for input and output design, then compile and run again, and observe the results.

(2) Create a server, use it request the user to enter the password, then open a file and transfer the file through the network connection. Create a customer connected to the server, assign it the appropriate password, then capture and save the file. Use localhost on your own machine (by calling inetaddress.getbyname (null) to generate local IP addresses 127.0.0.1) to test these two programs.

(3) Modify the procedure in the Exercise 2 to control multiple customers with a multi-thread mechanism.

(4) Modify JabberClient, prohibit output refresh, and observe the results.

(5) Based on Showhtml.java, create a block that makes it a door to password protection for a particular part of your Web site.

(6) (possibly some difficulty) Create a pair of client / server programs, using a DTAGRAM to pass a file from one machine to another (see the narrative of the end of this chapter data). (7) (possibly some difficulty) makes a modification of the VLOOKUP.JAVA program so that we can click on the result name, then the program will automatically get that name and copy it to the clipboard (so that we can easily paste itself. E-mail). It may be necessary to go back to study the chapter of the IO data stream, remember how to use the Java 1.1 clipboard.

转载请注明原文地址:https://www.9cbs.com/read-4694.html

New Post(0)
CopyRight © 2020 All Rights Reserved
Processed: 0.041, SQL: 9