Java中网络编程
以下内容引用自http://wiki.jikexueyuan.com/project/java/networking.html:
术语网络编程指编写跨多种设备(电脑)执行的,设备使用一个网络互相连接的程序。
J2SE API的java.net包包含了一个类的集合和提供底层通信细节的接口,允许编写专注解决即将到来的问题的程序。
java.net包为两种常用的网络协议提供支持:
- TCP:TCP代表传输控制协议,允许两个应用程序间的可靠通信。TCP通常在因特网协议上被使用,这被称为TCP/IP。
- UDP:UDP代表用户数据报协议,一个无连接的允许应用程序间数据包传输的协议。
以下两个主题提供更好的理解:
- 套接字编程:这是在网络中最广泛使用的概念并且被很详细地解释。
- URL处理:这将被个别地解释。
一、套接字编程
套接字利用TCP在两台电脑间提供通信机制。一个客户端程序在通信最后创建一个套接字并努力连接服务器套接字。
当连接建立时,服务器在通信结束时创建一个套接字对象。客户端和服务器现在可以通过从套接字读或者写来交流。
java.net.Socket类代表一个套接字,而且java.net.ServerSocket类为服务器程序提供了一种机制来监听客户端并和它们建立连接。
以下步骤发生在两台电脑使用套接字建立TCP连接时:
- 服务器实例化一个ServerSocket对象,指示通信将产生在哪个端口号;
- 服务器调用ServerSocket类的accept()方法。这个方法等待直到一个客户端在给定的端口上连接到服务器;
- 在服务器等待后,一个客户端实例化一个Socket对象,指定服务器名称和连接的端口号;
- Socket类的构造函数努力将客户端连接到指定的服务器和端口号。如果通信建立,客户端现在就拥有了一个能和服务器通信的Socket对象;
- 在服务器端,accept()方法在服务器上返回一个连接到客户端套接字的新的套接字。
在连接建立后,通信可以使用I/O流产生。每个套接字都有一个输出流和一个输入流。客户端的输出流连接到服务器端的输入流,客户端的输入流连接到服务器端的输出流。
TCP是一个双向的通信协议,所以数据可以在两个流同时发送。由以下提供完整的方法的类来实现套接字。
1、ServerSocket类方法
java.net.ServerSocket类被服务器应用程序使用来获得一个端口和监听客户端请求。
ServerSocket类有四个构造函数:
方法 | 描述 |
---|---|
public ServerSocket(int port) throws IOException | 尝试创建一个连接到指定端口的服务器套接字。如果端口已经连接到另一个应用程序那么将产生一个异常。 |
public ServerSocket(int port, int backlog) throws IOException | 和前一个构造函数相同,backlog参数指定了在等待队列中有多少传入的客户端要存储 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException | 和前一个构造函数相同,InetAddress参数指定了本地捆绑的IP地址。InetAddress用于有多个IP地址的服务器,允许服务器指定它的哪个IP地址 来接收客户端请求。 |
public ServerSocket() throws IOException | 创建一个为绑定服务器的套接字。当使用这个构造函数时,当准备好绑定服务器套接字时使用bind()方法。 |
如果ServerSocket的构造函数不抛出一个异常,这意味着应用程序已经成功地绑定到特定的端口并且准备好客户端的请求了。
这里是一些ServerSocket类的常见方法:
方法 | 描述 |
---|---|
public int getLocalPort() | 返回服务器套接字正在监听的端口。如果在构造函数中传入0作为端口号这个方法会是有用的,它会让服务器找一个端口。 |
public Socket accept() throws IOException | 等待一个即将到来的客户端。这个方法直到或者一个客户端连接到特定端口服务器,或者套接字到时为止时阻塞,假设超时的值已经使用 setSoTimeout()方法设置了。否则,这个方法将无限期阻塞。 |
public void setSoTimeout(int timeout) | 把超时的值设为服务器套接字在accept()内等待客户端的时间。 |
public void bind(SocketAddress host, int backlog) | 将套接字绑定在特定的服务器和SocketAddress对象的端口上。如果使用无参数的构造函数实例化一个ServerSocket对象,使用这个方法。 |
当ServerSocket调用accept()方法直到一个客户端连接才返回。在一个客户端确实连接后,ServerSocket在一个未指定的端口上创建一个新的套接字,并返回一个新套接字的引用。一个TCP连接现在就存在于客户端和服务器间了,通信就可以开始了。
2、Socket类方法
java.net.Socket类方法代表客户端和服务器都用来互相通信的套接字。客户端通过实例化而拥有一个Socket对象,然而服务器从accept()方法的返回值获得一个Socket对象。
Socket类有5个客户端用来连接到服务器的构造函数:
方法 | 描述 |
---|---|
public Socket(String host, int port) throws UnknownHostException, IOException | 这个方法努力连接到特定端口指定的服务器。如果这个构造函数不抛出一个异常,连接就是成功的并且客户端将会连接到服务器。 |
public Socket(InetAddress host, int port) throws IOException | 这个方法和之前的构造函数相同,除了主机由一个InetAddress对象表示。 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException | 连接到指定的主机和端口,在指定地址和端口上的本地主机创建一个套接字。 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException | 这个方法和前一个构造函数相同,除了主机由一个InetAddress对象而不是一个String表示。 |
public Socket() | 创建一个不连接的套接字。使用connect()方法来连接这个套接字到服务器。 |
当套接字构造函数返回时,它并不简单地实例化一个Socket对象,它实际上试图连接到指定的服务器和端口。
在Socket类中一些有趣的方法列举在此。注意客户端和服务器都有一个Socket对象,所以这些方法都能被客户端或者服务器调用。
方法 | 描述 |
---|---|
public void connect(SocketAddress host, int timeout) throws IOException | 这个方法将套接字连接到特定的主机。这个方法仅当使用无参数的构造函数实例化Socket时才需要。 |
public InetAddress getInetAddress() | 这个方法返回其套接字连接的其他电脑的地址。 |
public int getPort() | 返回远端的机器上套接字绑定的端口。 |
public int getLocalPort() | 返回本地机器上套接字绑定的端口。 |
public SocketAddress getRemoteSocketAddress() | 返回远程套接字的地址。 |
public InputStream getInputStream() throws IOException | 返回套接字的输入流。输入流连接到远程套接字的输出流。 |
public OutputStream getOutputStream() throws IOException | 返回套接字的输出流。输出流连接到远程套接字的输入流。 |
public void close() throws IOException | 关闭套接字,这使得这个Socket对象不再能够连接到任何服务器。 |
3、InetAddress类方法
这个类表示一个网络协议(IP)的地址。这些是在做套接字编程时将会用到的有用的方法:
方法 | 描述 |
---|---|
static InetAddress getByAddress(byte[] addr) | 考虑到原始的IP地址,返回一个InetAddress对象。 |
static InetAddress getByAddress(String host, byte[] addr) | 基于提供的主机名和IP地址创建一个InetAddress。 |
static InetAddress getByName(String host) | 考虑到主机名,决定一个主机的IP地址。 |
String getHostAddress() | 用文本表示返回的IP地址字符串。 |
String getHostName() | 获得这个IP地址的主机名。 |
static InetAddress InetAddress getLocalHost() | 返回本地主机。 |
String toString() | 将IP地址转换为字符串。 |
4、套接字客户端示例
下列的GreetingClient是一个利用一个套接字连接到服务器的客户端程序,它发送一个问候,并等待响应。
// File Name GreetingClient.java import java.net.*; import java.io.*; public class GreetingClient { public static void main(String [] args) { String serverName = args[0]; int port = Integer.parseInt(args[1]); try { System.out.println("Connecting to " + serverName + " on port " + port); Socket client = new Socket(serverName, port); System.out.println("Just connected to " + client.getRemoteSocketAddress()); OutputStream outToServer = client.getOutputStream(); DataOutputStream out = new DataOutputStream(outToServer); out.writeUTF("Hello from " + client.getLocalSocketAddress()); InputStream inFromServer = client.getInputStream(); DataInputStream in = new DataInputStream(inFromServer); System.out.println("Server says " + in.readUTF()); client.close(); }catch(IOException e) { e.printStackTrace(); } } }
5、套接字服务器示例
下列的GreetingServer程序是一个使用Socket类来监听由命令行参数指定的端口号上的客户端的服务器应用程序的一个例子:
// File Name GreetingServer.java import java.net.*; import java.io.*; public class GreetingServer extends Thread { private ServerSocket serverSocket; public GreetingServer(int port) throws IOException { serverSocket = new ServerSocket(port); serverSocket.setSoTimeout(10000); } public void run() { while(true) { try { System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "..."); Socket server = serverSocket.accept(); System.out.println("Just connected to " + server.getRemoteSocketAddress()); DataInputStream in = new DataInputStream(server.getInputStream()); System.out.println(in.readUTF()); DataOutputStream out = new DataOutputStream(server.getOutputStream()); out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress() + "\nGoodbye!"); server.close(); }catch(SocketTimeoutException s) { System.out.println("Socket timed out!"); break; }catch(IOException e) { e.printStackTrace(); break; } } } public static void main(String [] args) { int port = Integer.parseInt(args[0]); try { Thread t = new GreetingServer(port); t.start(); }catch(IOException e) { e.printStackTrace(); } } }
编译客户端和服务器然后像这样开始服务器:
$ java GreetingServer 6066 Waiting for client on port 6066...
像这样检查客户端程序:
$ java GreetingClient localhost 6066 Connecting to localhost on port 6066 Just connected to localhost/127.0.0.1:6066 Server says Thank you for connecting to /127.0.0.1:6066 Goodbye!
二、URL处理
URL代表统一资源定位器,代表万维网上的资源,如网页或FTP目录。
URL可以分为几部分,如下所示:
protocol://host:port/path?query#ref
协议的例子包括HTTP,HTTPS,FTP和File。该路径也称为文件名,主机也称为权限。
以下是协议为HTTP的网页的URL:
https://www.amrood.com/index.htm?language=en#j2se
请注意,此URL不指定端口,在这种情况下,将使用该协议的默认端口。使用HTTP,默认端口为80。
1、URL类方法
在Java中java.net.URL类代表一个URL,并拥有一套完整的方法来操纵URL。
URL类有几个用于创建URL的构造函数,包括以下内容:
方法 | 说明 |
---|---|
public URL(String protocol, String host, int port, String file) throws MalformedURLException |
通过组合给定的部分创建一个URL。 |
public URL(String protocol, String host, String file) throws MalformedURLException |
与以前的构造函数相同,除了使用给定协议的默认端口。 |
public URL(String url) throws MalformedURLException |
从给定的字符串创建一个URL。 |
public URL(URL context, String url) throws MalformedURLException |
通过一起解析URL和String参数来创建URL。 |
URL类包含许多访问正在表示的URL的各个部分的方法。URL类中的一些方法包括以下内容:
方法 | 说明 |
---|---|
public String getPath() |
返回URL的路径。 |
public String getQuery() |
返回URL的查询部分。 |
public String getAuthority() |
返回URL的权限。 |
public int getPort() |
返回URL的端口。 |
public int getDefaultPort() |
返回URL协议的默认端口。 |
public String getProtocol() |
返回URL的协议。 |
public String getHost() |
返回URL的主机。 |
public String getHost() |
返回URL的主机。 |
public String getFile() |
返回URL的文件名。 |
public String getRef() |
返回URL的引用部分。 |
public URLConnection openConnection() throws IOException |
打开与URL的连接,允许客户端与资源进行通信。 |
示例:
以下URLDemo程序演示了URL的各个部分。URL在命令行中输入,URLDemo程序输出给定URL的每个部分。
// File Name : URLDemo.java import java.net.*; import java.io.*; public class URLDemo { public static void main(String [] args) { try { URL url = new URL("https://www.amrood.com/index.htm?language=en#j2se"); System.out.println("URL is " + url.toString()); System.out.println("protocol is " + url.getProtocol()); System.out.println("authority is " + url.getAuthority()); System.out.println("file name is " + url.getFile()); System.out.println("host is " + url.getHost()); System.out.println("path is " + url.getPath()); System.out.println("port is " + url.getPort()); System.out.println("default port is " + url.getDefaultPort()); System.out.println("query is " + url.getQuery()); System.out.println("ref is " + url.getRef()); }catch(IOException e) { e.printStackTrace(); } } } //此程序的示例运行将产生以下结果: URL is https://www.amrood.com/index.htm?language=en#j2se protocol is http authority is www.amrood.com file name is /index.htm?language=en host is www.amrood.com path is /index.htm port is -1 default port is 80 query is language=en ref is j2se
2、URLConnections类方法
openConnection()方法返回一个java.net.URLConnection,一个抽象类,其子类表示各种类型的URL连接。
例如:
-
如果连接到协议为HTTP的URL,则openConnection()方法返回一个HttpURLConnection对象。
-
如果连接到表示JAR文件的URL,则openConnection()方法返回一个JarURLConnection对象等。
URLConnection类有许多方法用于设置或确定有关连接的信息,包括以下内容:
方法 | 说明 |
---|---|
Object getContent() |
检索此URL连接的内容。 |
Object getContent(Class[] classes) |
检索此URL连接的内容。 |
String getContentEncoding() |
返回content-encoding头域的值。 |
int getContentLength() |
返回content-length头字段的值。 |
String getContentType() |
返回content-type头域的值。 |
int getLastModified() |
返回最后修改的头字段的值。 |
long getExpiration() |
返回过期标头字段的值。 |
long getIfModifiedSince() |
返回此对象的ifModifiedSince字段的值。 |
public void setDoInput(boolean input) |
通过true表示连接将用于输入。默认值为true,因为客户端通常从URLConnection读取。 |
public void setDoOutput(boolean output) |
通过true表示连接将用于输出。默认值为false,因为许多类型的URL不支持写入。 |
public InputStream getInputStream() throws IOException |
返回用于从资源读取的URL连接的输入流。 |
public OutputStream getOutputStream() throws IOException |
返回用于写入资源的URL连接的输出流。 |
public URL getURL() |
返回此URLConnection对象连接到的URL。 |
示例:
以下URLConnectionDemo程序连接到从命令行输入的URL。
如果URL表示HTTP资源,则将连接转换为HttpURLConnection,资源中的数据一次读取一行。
// File Name : URLConnDemo.java import java.net.*; import java.io.*; public class URLConnDemo { public static void main(String [] args) { try { URL url = new URL("https://www.amrood.com"); URLConnection urlConnection = url.openConnection(); HttpURLConnection connection = null; if(urlConnection instanceof HttpURLConnection) { connection = (HttpURLConnection) urlConnection; }else { System.out.println("Please enter an HTTP URL."); return; } BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String urlString = ""; String current; while((current = in.readLine()) != null) { urlString += current; } System.out.println(urlString); }catch(IOException e) { e.printStackTrace(); } } } //此程序的示例运行将产生以下结果: $ java URLConnDemo .....a complete HTML content of home page of amrood.com.....
测试工程:https://github.com/easonjim/5_java_example/tree/master/javabasicstest/test26