Java I/O: Chapter 5. Network Streams
Java I/O
By Elliotte Rusty Harold
1st Edition March 1999
1-56592-485-1, Order Number: 4851
596 pages, $34.95
Chapter 5.
Network Streams
In this chapter:
URLs
From its first days, Java has had the network in mind, more so than any other common programming language. Java is the first programming language to provide as much support for network I/O as it does for file I/O, perhaps even more--Java's
URL,URLConnection,Socket, andServerSocketclasses are all fertile sources of streams. The exact type of the stream used by a network connection is typically hidden inside the undocumentedsunclasses. Thus, network I/O relies primarily on the basicInputStreamandOutputStreammethods, which you can wrap with any higher-level stream that suits your needs: buffering, cryptography, compression, or whatever your application requires.URLs
The
java.net.URLclass represents a Uniform Resource Locator like http://metalab.unc.edu/javafaq/. Each URL unambiguously identifies the location of a resource on the Internet. TheURLclass has four constructors. All are declared to throwMalformedURLException, a subclass ofIOException.public URL(String u) throws MalformedURLExceptionpublic URL(String protocol, String host, String file)throws MalformedURLExceptionpublic URL(String protocol, String host, int port, String file)throws MalformedURLExceptionpublic URL(URL context, String u) throws MalformedURLExceptionA
MalformedURLExceptionis thrown if the constructor's arguments do not specify a valid URL. Often this means a particular Java implementation does not have the right protocol handler installed. Thus, given a complete absolute URL like http://www.poly.edu/schedule/fall97/bgrad.html#cs, you construct aURLobject like this:URL u = null;try {u = new URL("http://www.poly.edu/schedule/fall97/bgrad.html#cs");}catch (MalformedURLException e) { }You can also construct the
URLobject by passing its pieces to the constructor:URL u = null;try {u = new URL("http", "www.poly.edu", "/schedule/fall97/bgrad.html#cs");}catch (MalformedURLException e) { }You don't normally need to specify a port for a URL; most protocols have default ports. For instance, the HTTP port is 80. Sometimes the port used does change, and in that case you can use the third constructor:
URL u = null;try {u = new URL("http", "www.poly.edu", 80, "/schedule/fall97/bgrad.html#cs");}catch (MalformedURLException e) { }Finally, many HTML files contain relative URLs. The fourth constructor in the previous code creates URLs relative to a given URL and is particularly useful when parsing HTML. For example, the following code creates a URL pointing to the file 08.html, taking the rest of the URL from
u1:URL u1, u2;try {u1 = new URL("http://metalab.unc.edu/javafaq/course/week12/07.html");u2 = new URL(u1, "08.html");}catch (MalformedURLException e) { }Once a
URLobject has been constructed, there are two ways to retrieve its data. TheopenStream()method returns a raw stream of bytes from the source. ThegetContent()method returns a Java object that represents the data. When you callgetContent(), Java looks for a content handler that matches the MIME type of the data. It is theopenStream()method that is of concern in this book.The
openStream()method makes a socket connection to the server and port specified in the URL. It returns an input stream from which you can read the data at that URL, allowing you to download data from the server. Any headers that come before the actual data or file requested are stripped off before the stream is opened. You get only raw data.public final InputStream openStream() throws IOExceptionYou read from the input stream using an input stream or reader class. For example:
try {URL u = new URL("http://www.amnesty.org/");InputStream in = u.openStream();int b;while ((b = in.read()) != -1) {System.out.write(b);}}catch (MalformedURLException e) {System.err.println(e);}catch (IOException e) {System.err.println(e);}Applets running under the control of a security manager--that is untrusted applets that run inside a web browser--are normally only allowed to connect back to the host they were downloaded from. This host can be determined from the URL returned by the
getCodeBase()method of theAppletclass. Attempts to connect to other hosts throw security exceptions. You can create URLs that point to other hosts, but you may not download data from them usingopenStream()or any other method. (This security restriction for applets applies to any network connection, regardless of how you get it.)Example 5-1 reads a series of URLs from the command line. The program connects to the specified URLs, downloads the data, and copies it to
System.out.Example 5-1:
The URLTyper Programimport java.net.*;import java.io.*;import com.macfaq.io.*;public class URLTyper {public static void main(String[] args) {if (args.length == 0) {System.err.println("Usage: java URLTyper url1 url2 ...");return;}for (int i = 0; i < args.length; i++) {if (args.length > 1) {System.out.println(args[i] + ":");}try {URL u = new URL(args[i]);InputStream in = u.openStream();StreamCopier.copy(in, System.out);in.close();}catch (MalformedURLException e) {System.err.println(e);}catch (IOException e) {System.err.println(e);}}}}For example, here are the first few lines you see when you connect to http://www.oreilly.com/:
D:\JAVA\ioexamples\05>java URLTyper http://www.oreilly.com/<HTML><HEAD><META name="keywords" content="computer books, technical books, UNIX, unix,Perl, Java, Linux, Internet, Web, C, C++, Windows, Windows NT, Security,Sys Admin, System Administration, Oracle, design, graphics, online books,online courses, Perl Conference, Web-based training, Software, open source,free software"><META name="description" content="O'Reilly is a leader in technical andcomputer book documentation for UNIX, Perl, Java, Linux, Internet,Web, C, C++, Windows, Windows NT, Security, Sys Admin, SystemAdministration, Oracle, Design & Graphics, Online Books, Online Courses,Perl Conference, Web-based training, and Software"><TITLE>www.oreilly.com -- Welcome to O'Reilly & Associates!</TITLE>Most network connections, even on LANs, are slower and less reliable sources of data than files. Connections across the Internet are even slower and less reliable, and connections through a modem are slower and less reliable still. One way to enhance performance under these conditions is to buffer the data: to read as much data as you can into a temporary storage array inside the class, then parcel it out as needed. In the next chapter, you'll learn about the
BufferedInputStreamclass that does exactly this.URL Connections
URL connections are closely related to URLs, as their name implies. Indeed, you get a reference to a
URLConnectionby using theopenConnection()method of aURLobject; in many ways, theURLclass is only a wrapper around theURLConnectionclass. However, URL connections provide more control over the communication between the client and the server. In particular, URL connections provide not just input streams by which the client can read data from the server, but also output streams to send data from the client to the server. This is essential for protocols like mailto.The
java.net.URLConnectionclass is an abstract class that handles communication with different kinds of servers, like FTP servers and web servers. Protocol-specific subclasses ofURLConnection, hidden inside thesunclasses, handle different kinds of servers.Reading Data from URL Connections
URL connections take place in five steps:
- The
URLobject is constructed.- The
openConnection()method of theURLobject creates theURLConnectionobject.- The parameters for the connection and the request properties that the client sends to the server are set up.
- The
connect()method makes the connection to the server, perhaps using a socket for a network connection or a file input stream for a local connection. The response header information is read from the server.- Data is read from the connection by using the input stream returned by
getInputStream()or through a content handler withgetContent(). Data can be sent to the server using the output stream provided bygetOutputStream().This scheme is very much based on the HTTP/1.0 protocol. It does not fit other schemes that have a more interactive "request, response, request, response, request, response" pattern instead of HTTP/1.0's "single request, single response, close connection" pattern. In particular, FTP and even HTTP/1.1 aren't well suited to this pattern. I wouldn't be surprised to see this replaced with something more general in a future version of Java.
URLConnectionobjects are not constructed directly in your own programs. Instead, you create aURLfor the particular resource, and call thatURL'sopenConnection()method. This gives you aURLConnection. Then thegetInputStream()method returns an input stream that reads data from the URL. (TheopenStream()method of theURLclass is just a thin veneer over thegetInputStream()method of theURLConnectionclass.) For example:try {URL u = new URL("http://www.digitalthink.com/");URLConnection uc = u.openConnection();uc.connect();InputStream in = uc.getInputStream();//...}catch (IOException e) { //...If the connection cannot be opened (possibly because the remote host is unreachable), an
IOExceptionis thrown. Example 5-2 reads a URL from the command line, opens a connection to that URL, then prints the data returned by the server at that URL. This is similar to Example 5-1;WebCatdiffers primarily in that it uses aURLConnectioninstead of aURL.Example 5-2:
The WebCat Programimport java.net.*;import java.io.*;import com.macfaq.io.*;public class WebCat {public static void main(String[] args) {if (args.length == 0) {System.err.println("Usage: java WebCat url1 url2 ...");return;}for (int i = 0; i < args.length; i++) {if (i > 0 && i < args.length) {System.out.println();System.out.println("----------------------");System.out.println();}System.out.println(args[i] + ":");try {URL u = new URL(args[i]);URLConnection uc = u.openConnection();uc.connect();InputStream in = uc.getInputStream();StreamCopier.copy(in, System.out);in.close();}catch (IOException e) {System.err.println(e);}} // end for}}Writing Data on URL Connections
Writing data to a
URLConnectionis similar to reading data. However, you must first inform theURLConnectionthat you plan to use it for output, and then, instead of getting the connection's input stream and reading from it, you get the connection's output stream and write to it. This is commonly used to talk to CGIs that use the POST method or to store files on web servers through the PUT method, or to communicate with a Java servlet running on a server. Here are the steps for writing data on aURLConnection:
- Construct the
URLobject.- Call the
openConnection()method of theURLobject to create theURLConnectionobject.- Pass
truetosetDoOutput() to indicate that thisURLConnectionwill be used for output.- If you also want to read input from the stream, invoke
setDoInput(true)to indicate that thisURLConnectionwill be used for input.- Create the data you want to send, preferably as a byte array.
- Call
getOutputStream()to get an output stream object. Write the byte array calculated in step 5 onto the stream.- Close the output stream.
- Call
getInputStream()to get an input stream object. Read and write it as usual.Example 5-3 uses these steps to implement a simple mail client. It forms a mailto URL from an email address entered on the command line. Input for the message is copied from
System.inonto the output stream of theURLConnectionusing aStreamCopier. The end of the message is indicated by the end-of-stream character.Example 5-3:
The MailClient Classimport java.net.*;import java.io.*;import com.macfaq.io.*;public class MailClient {public static void main(String[] args) {if (args.length == 0) {System.err.println("Usage: java MailClient username@host.com");return;}try {URL u = new URL("mailto:" + args[0]);URLConnection uc = u.openConnection();uc.setDoOutput(true);uc.connect();OutputStream out = uc.getOutputStream();StreamCopier.copy(System.in, out);out.close();}catch (IOException e) {System.err.println(e);}}}For example, to send email to the author of this book:
% java MailClient elharo@metalab.unc.eduhi there!^D
MailClientsuffers from a few restrictions. The proper way to detect the end of the message is to look for a period on a line by itself. Proper or not, that style of user interface is really antiquated, so I didn't bother to implement it. To do so properly, you'll need to use aReaderor aWriter; they're discussed in Chapter 15, Readers and Writers. Furthermore, it only works in Java environments that support the mailto protocol; thus, it works under Sun's JDK, but may not work in other environments. It also requires that the local host be running an SMTP server, or that the system propertymailhostmust contain the name of an accessible SMTP server, or that a machine in the local domain named mailhost be running an SMTP server. Finally, the security manager must permit network connections to that server, although this is not normally a problem in an application.Sockets
Before data is sent across the Internet from one host to another, it is split into packets of varying but finite size called datagrams. Datagrams range in size from a few dozen bytes to about 60,000 bytes. Anything larger, and often things smaller, must be split into smaller pieces before it can be transmitted. The advantage of this scheme is that if one packet is lost, it can be retransmitted without requiring redelivery of all other packets. Furthermore, if packets arrive out of order, they can be reordered at the receiving end of the connection.
Fortunately, packets are invisible to the Java programmer. The host's native networking software splits data into packets on the sending end and reassembles packets on the receiving end. Instead, the Java programmer is presented with a higher-level abstraction called a socket. The socket represents a reliable connection for the transmission of data between two hosts. It isolates you from the details of packet encodings, lost and retransmitted packets, and packets that arrive out of order. A socket performs four fundamental operations:
- Connect to a remote machine
- Send data
- Receive data
- Close the connection
A socket may not be connected to more than one host at a time. However, a socket may both send data to and receive data from the host to which it's connected.
The
java.net.Socketclass is Java's interface to a network socket and allows you to perform all four fundamental socket operations. It provides raw, uninterpreted communication between two hosts. You can connect to remote machines; you can send data; you can receive data; you can close the connection. No part of the protocol is abstracted out, as it is withURLandURLConnection. The programmer is completely responsible for the interaction between the network client and the server. To create a connection, you call one of theSocketconstructors, specifying the host you want to connect to. EachSocketobject is associated with exactly one remote host. To connect to a different host, you must create a newSocketobject:public Socket(String host, int port) throws UnknownHostException, IOExceptionpublic Socket(InetAddress address, int port) throws IOExceptionpublic Socket(String host, int port, InetAddress localAddr, int localPort)throws IOExceptionpublic Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOExceptionThe
hostis a string like "www.oreilly.com" or "metalab.unc.edu", which specifies the particular host to connect to. It may even be a numeric, dotted quad string like "199.1.32.90". This argument may also be passed as ajava.net.InetAddressobject.The
portargument is the port on the remote host to connect to. A computer's network interface is logically subdivided into 65,536 different ports. As data traverses the Internet in packets, each packet carries not only the address of the host but also the port on that host at which it's aimed. The host is responsible for reading the port number from each packet it receives to decide which program should receive that chunk of data. Many services run on well-known ports. This means that the protocol specifies that the service should or must use a particular port--for example, HTTP servers generally listen on port 80.The optional
localAddressandlocalPortarguments specify which address and port on the local host the socket is to connect from, assuming more than one is available. Most hosts have many available ports but only one address. These two arguments are optional. If they're left out, the constructor will choose reasonable values.Sending and receiving data across a socket is accomplished with output and input streams. These are the methods to get both streams for the socket:
public InputStream getInputStream() throws IOExceptionpublic OutputStream getOutputStream() throws IOExceptionThere's also a method to close a socket:
public synchronized void close() throws IOExceptionThis effectively closes the socket's input and output streams as well. Any attempt to read from or write to them after the socket is closed will throw an
IOException.Example 5-4 is yet another program that connects to a web server and downloads a specified URL. However, since this one uses raw sockets, it needs to both send the HTTP request and read the headers in the response. These are not parsed away as they are by the
URLandURLConnectionclasses; you use an output stream to send the request explicitly and an input stream to read the data back, including HTTP headers. Only HTTP URLs are supported.Example 5-4:
The SocketTyper Programimport java.net.*;import java.io.*;import com.macfaq.io.*;public class SocketTyper {public static void main(String[] args) {if (args.length == 0) {System.err.println("Usage: java SocketTyper url1 url2 ...");return;}for (int i = 0; i < args.length; i++) {if (args.length > 1) {System.out.println(args[i] + ":");}try {URL u = new URL(args[i]);if (!u.getProtocol().equalsIgnoreCase("http")) {System.err.println("Sorry, " + u.getProtocol() +" is not yet supported.");break;}String host = u.getHost();int port = u.getPort();String file = u.getFile();// default portif (port <= 0) port = 80;Socket s = new Socket(host, port);String request = "GET " + file + " HTTP/1.0\r\n"+ "User-Agent: MechaMozilla\r\nAccept: text/*\r\n\r\n";// This next line is problematic on non-ASCII systemsbyte[] b = request.getBytes();OutputStream out = s.getOutputStream();InputStream in = s.getInputStream();out.write(b);out.flush();StreamCopier.copy(in, System.out);in.close();out.close();s.close();}catch (MalformedURLException e) {System.err.println(e);}catch (IOException e) {System.err.println(e);}}}}For example, when
SocketTyperconnects to http://www.oreilly.com/, here is what you see:% java SocketTyper http://www.oreilly.com/HTTP/1.0 200 OKServer: WN/1.15.1Date: Sun, 09 Aug 1998 20:05:03 GMTLast-modified: Fri, 07 Aug 1998 23:44:36 GMTContent-type: text/htmlTitle: www.oreilly.com -- Welcome to O'Reilly & Associates!Link: <mailto:webmaster@ora.com>; rev="Made"<HTML><HEAD><META name="keywords" content="computer books, technical books, UNIX, unix,Perl, Java, Linux, Internet, Web, C, C++, Windows, Windows NT, Security,Sys Admin, System Administration, Oracle, design, graphics, online books,online courses, Perl Conference, Web-based training, Software, open source,free software">Notice the header lines you didn't see in Example 5-1. When you use the
URLclass to download a web page, the associated protocol handler never shows you the HTTP header.Server Sockets
There are two ends to each connection: the client, which initiates the connection, and the server, which responds to the connection. So far, we've only discussed the client side and assumed that a server existed out there for the client to talk to. To implement a server, you need to write a program that waits for other hosts to connect to it. A server socket binds to a particular port on the local machine (the server); once it has successfully bound to a port, it listens for incoming connection attempts from remote machines (the clients). When the server detects a connection attempt, it accepts the connection. This creates a socket between the two machines over which the client and the server communicate.
Many clients can connect to a port on the server simultaneously. Incoming data is distinguished by the port to which it is addressed and the client host and port from which it came. The server can tell for which service (like HTTP or FTP) the data is intended by inspecting the port. It knows where to send any response by looking at the client address and port stored with the data.
No more than one server socket can listen to a particular port at one time. Therefore, since a server may need to handle many connections at once, server programs tend to be heavily multithreaded. Generally, the server socket listening on the port only accepts the connections. It passes off the actual processing of each connection to a separate thread. Incoming connections are stored in a queue until the server can accept them. On most systems, the default queue length is between 5 and 50. Once the queue fills up, further incoming connections are refused until space in the queue opens up.
The
java.net.ServerSocketclass represents a server socket. Three constructors let you specify the port to bind to, the queue length for incoming connections, and the IP address:public ServerSocket(int port) throws IOExceptionpublic ServerSocket(int port, int backlog) throws IOExceptionpublic ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOExceptionNormally, you specify only the port you want to listen on:
try {ServerSocket ss = new ServerSocket(80);}catch (IOException e) {System.err.println(e);}When you create a
ServerSocketobject, it attempts to bind to the port on the local host given by the port argument. If another server socket is already listening to the port, then ajava.net.BindException, a subclass ofIOException, is thrown. No more than one process or thread can listen to a particular port at a time. This includes non-Java processes or threads. For example, if there's already an HTTP server running on port 80, you won't be able to bind to port 80. On Unix systems (but not Windows or the Mac) your program must be running as root to bind to a port between 1 and 1023.0 is a special port number. It tells Java to pick an available port. You can then find out what port it's picked with the
getLocalPort()method:public int getLocalPort()This is useful if the client and the server have already established a separate channel of communication over which the chosen port number can be communicated. For example, the FTP protocol uses two sockets. The initial connection is made by the client to the server to send commands. One of the commands sent tells the server the port number on which the client is listening. The server then connects to the client on this port to send data.
Once you have a
ServerSocket, you need to wait for incoming connections. You do this by calling theaccept()method, which blocks until a connection attempt occurs and then returns aSocketthat you can use to communicate with the client. Theclose()method terminates theServerSocket.public Socket accept() throws IOExceptionpublic void close() throws IOExceptionThat's pretty much all there is, except for a few methods dealing with socket options and some other details. In particular, there aren't methods for getting input and output streams. Instead,
accept()returns aSocketobject: you call theSocket'sgetInputStream()orgetOutputStream()method. For example:try {ServerSocket ss = new ServerSocket(2345);Socket s = ss.accept();OutputStream out = s.getOutputStream();// Send data to the client.s.close();}catch (IOException e) {System.err.println(e);}Notice in this example, I closed the
Sockets, not theServerSocketss.ssis still bound to port 2345. You get a new socket for each connection and reuse the server socket. For example, the next code fragment repeatedly accepts connections:try {ServerSocket ss = new ServerSocket(2345);while (true) {Socket s = ss.accept();OutputStream out = s.getOutputStream();// send data to the clients.close();}}catch (IOException e) {System.err.println(e);}The program in Example 5-5 reads a port number from the command line. It listens on that port for incoming connections. When it detects one, it answers back with the client's address and port and its own. Then it closes the connection.
Example 5-5:
The HelloServer Programimport java.net.*;import java.io.*;public class HelloServer {public final static int defaultPort = 2345;public static void main(String[] args) {int port = defaultPort;try {port = Integer.parseInt(args[0]);}catch (Exception e) {}if (port <= 0 || port >= 65536) port = defaultPort;try {ServerSocket ss = new ServerSocket(port);while (true) {try {Socket s = ss.accept();String response = "Hello " + s.getInetAddress() + " on port "+ s.getPort() + "\r\n";response += "This is " + s.getLocalAddress() + " on port "+ s.getLocalPort() + "\r\n";OutputStream out = s.getOutputStream();out.write(response.getBytes());out.flush();s.close();}catch (IOException e) {}}}catch (IOException e) {System.err.println(e);}}}Here's some output from our server. The server is running on utopia.poly.edu. The client is connecting from titan.oit.unc.edu. Note how the port from which the connection comes changes each time; like most client programs, the telnet program picks a random local port for outgoing connections:
% telnet utopia.poly.edu 2545Trying 128.238.3.21...Connected to utopia.poly.edu.Escape character is '^]'.Hello titan.oit.unc.edu/152.2.22.14 on port 50361This is utopia.poly.edu/128.238.3.21 on port 2545Connection closed by foreign host.% telnet utopia.poly.edu 2545Trying 128.238.3.21...Connected to utopia.poly.edu.Escape character is '^]'.Hello titan.oit.unc.edu/152.2.22.14 on port 50362This is utopia.poly.edu/128.238.3.21 on port 2545Connection closed by foreign host.URLViewer
Example 5-6 is an improved version of the
URLVieweryou first encountered in Chapter 2. This is a simple application that provides a window in which you can view the contents of a URL. It assumes that those contents are more or less ASCII text. (In future chapters, I'll remove that restriction.) Figure 5-1 shows the result. Our application has a text area in which the user can type a URL, a Load button that the user uses to load the specified URL, and aStreamedTextAreacomponent that displays the text from the URL. Each of these corresponds to a field in theURLViewerclass.
Figure 5-1.
The URLViewer
Example 5-6:
The URLViewer Programimport java.awt.*;import java.awt.event.*;import java.io.*;import java.net.*;import com.macfaq.awt.*;import com.macfaq.io.*;public class URLViewer extends Frameimplements WindowListener, ActionListener {TextField theURL = new TextField();Button loadButton = new Button("Load");StreamedTextArea theDisplay = new StreamedTextArea();public URLViewer() {super("URL Viewer");}public void init() {this.add("North", theURL);this.add("Center", theDisplay);Panel south = new Panel();south.add(loadButton);this.add("South", south);theURL.addActionListener(this);loadButton.addActionListener(this);this.addWindowListener(this);this.setLocation(50, 50);this.pack();this.show();}public void actionPerformed(ActionEvent evt) {try {URL u = new URL(theURL.getText());InputStream in = u.openStream();OutputStream out = theDisplay.getOutputStream();StreamCopier.copy(in, out);in.close();out.close();}catch (MalformedURLException ex) {theDisplay.setText("Invalid URL");}catch (IOException ex) {theDisplay.setText("Invalid URL");}}public void windowClosing(WindowEvent e) {this.setVisible(false);this.dispose();}public void windowOpened(WindowEvent e) {}public void windowClosed(WindowEvent e) {}public void windowIconified(WindowEvent e) {}public void windowDeiconified(WindowEvent e) {}public void windowActivated(WindowEvent e) {}public void windowDeactivated(WindowEvent e) {}public static void main(String args[]) {URLViewer me = new URLViewer();me.init();}}The
URLViewerclass itself extendsFrame. An alternative would have been to extendPanel, which would have allowedURLViewerobjects to be embedded in other containers. However, this application seemed big enough to justify exclusive use of a window.URLViewerimplements theWindowListenerinterface to enable the user to close the window by clicking in the close box. Only thewindowClosing()method is nontrivial. The other six window methods are do-nothing methods required to satisfy the contract of theWindowListenerinterface.The
init()method builds the interface and displays the window. This is invoked by themain()method, which constructs a newURLViewerobject. The constructor is quite simple, merely passing the title of the frame to the superclass constructor.The streamed text area is filled when the user clicks the Load button or hits Return inside the URL text field. The
URLViewerobject listens to both of these components. TheURLViewer'sactionPerformed()method constructs a URL from the text in the text field, then opens an input stream from the URL in the text field. Next,StreamCopierfrom Chapter 3, Input Streams pours the data from the URL's input stream into the text area's output stream. When that's finished, both streams are closed.Back to: Java I/O
O'Reilly Home |
O'Reilly Bookstores |
How to Order |
O'Reilly Contacts
International |
About O'Reilly |
Affiliated Companies
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com


浙公网安备 33010602011771号