1 简介
使用Java独有的一套用于网络的API来开发网络软件是很方便和强大的,它包含网络低层API和网络高层API。其中对应网络应用层包含URL和URLEncoding,对应传输层有Socket和DatagramSocket,对应IP层有InetAddress,物理层没有对应类,也不需要有。
2 套接字相关类
2、1 InetAddress
InetAddress类在Java网络API编程中扮演了一个的重要角色,完成了对IP地址的对象封装。InetAddress类描述了32位或64位IP地址,即Inet4Address类和Inet6Address类。此类对象的创建是通过自己类的工厂方法来进行的,所谓工厂方法是指可以产生不同派生类对象的专门类中这些用来产生此对象的方法。
具体为
getAllByName(String host)
getByAddress(byte [] addr)
getByAddress(String host, byte [] addr)
getByName(String host)
getLocalHost()
上面的方法都要返回一个或多个Inet4Address/Inet6Address对象的引用,调用者不需要知道引用的子类型,调用者可以使用返回的引用,来调用InetAddress对象的非静态方法(包括子类型的多态)以确保重载方法被调用。
具体的使用为:
InetAddress和它的子类型对象处理主机名到主机IPv4或IPv6地址的转换。一旦获得了InetAddress子类对象的引用就可以调用InetAddress的各种方法来获得InetAddress子类对象中的IP地址信息。如:
getCanonicalHostName(): 从域名服务中获得标准的主机名
getHostAddress(): 获得IP地址
getHostName(): 获得主机名
isLoopbackAddress(): 判断IP地址是否是一个回环(loopback)地址。
如
import java.net.*;
class InetAddressDemo
{
public static void main (String [] args) throws UnknownHostException
{
String host = "localhost";
if (args.length == 1)
host = args [0];
InetAddress ia = InetAddress.getByName (host);
System.out.println ("Host Name = " + ia.getCanonicalHostName ());
System.out.println ("Host Address = " + ia.getHostAddress ());
System.out.println ("Host Name = " + ia.getHostName ());
System.out.println ("Is Loopback Address = " + ia.isLoopbackAddress ());
}
}
2、2 Scoket套接字
Java的网络API依*Socket进行通讯。Socket可以看成在两个程序进行通讯连接中的端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
所谓客户机是指主动请求的一方,需要指定请求机器的地址和端口号,而服务器是被动的一方,需要客户机的请求才能获取客户机的地址和端口号从而和其通信。也就是说,如果在服务器没有服务的情况下,客户机先行请求会产生异常。
在双方的具体通信中,两者都可以向对方发送信息。
假设在网络中添加了多个主机,那么怎么知道信息被正确传送到目的主机而不是被传送到其他主机中了呢?
因为基于TCP/IP网络中的每一个主机均被赋予了一个唯一的IP地址;
假设有多个程序被加入网络的某台主机中,那么由其他主机传来的信息如何能被正确的传给目的程序而不是传给其他程序呢?
因为每一个基于TCP/IP网络通讯的程序都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入输出信息,端口号是一个16位无符号整数,范围是0-65535。低于256的断口号保留给标准应用程序,比如http的端口号就是80,每一个套接字都组合进了IP地址、端口、端口号,这样形成的整体就可以区别每一个套接字。
客户端流套接字的初始化代码将IP地址和端口号传递给客户端主机的网络管理软件,管理软件将IP地址和端口号通过NIC传递给服务器端主机;服务器端主机读到经过NIC传递来的数据,然后查看服务器程序是否处于监听状态,这种监听依然是通过套接字和端口来进行的;如果服务器程序处于监听状态,那么服务器端网络管理软件就向客户机网络管理软件发出一个积极的响应信号,接收到响应信号后,客户端流套接字初始化代码就给客户程序建立一个端口号,并将这个端口号传递给服务器程序的套接字(服务器程序将使用这个端口号识别传来的信息是否是属于客户程序)同时完成流套接字的初始化。如果服务器程序没有处于监听状态,那么服务器端网络管理软件将给客户端传递一个消极信号,收到这个消极信号后,客户程序的流套接字初始化代码将抛出一个异常对象并且不建立通讯连接,也不创建流套接字对象。这种情形就像打电话一样,当有人的时候通讯建立,否则电话将被挂起。
无论何时,在两个网络应用程序之发间送和接收信息时都需要建立一个可*的连接。依*TCP协议来保证信息正确到达目的地。IP包有可能在网络中丢失或者在传送过程中发生错误,任何一种情况发生,作为接受方的TCP将联系发送方TCP重新发送这个IP包。所以可*性较高,但是由于包传递失败会重传,所以并不适合实时媒体的通信。
具体的使用为:
当客户程序需要与服务器程序通讯的时候,客户程序在客户机创建一个socket对象。
Socket类有两个常用的构造函数是:Socket(InetAddress addr, int port)和Socket(String host, int port)
对于第二个构造函数,如果没有IP地址与host参数相一致,那么将抛出UnknownHostException异常对象。
两个函数都通过参数port获得服务器的端口号。假设已经建立连接了,网络API将在客户端基于Socket的流套接字中绑定客户程序的IP地址和任意一个端口号,否则两个函数都会抛出一个IOException对象。
一旦创建了一个Socket对象,那么它可能通过调用Socket的getInputStream()方法从服务程序获得输入流读传送来的信息。也可通过调用Socket的getOutputStream()方法获得输出流来发送消息。在读写活动完成之后,客户程序调用close()方法关闭流和流套接字。
而ServerSocket对象等待客户端建立连接,accept方法用于等待客户端触发通信。
一个简单的网络通信程序,将客户机的信息全部发送给服务器
客户端:
import java.io.*;
import java.net.*;
public class TalkClient
{
public static void main(String args[])
{
try
{
Socket socket=new Socket("127.0.0.1",4701);
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
PrintWriter os=new PrintWriter(socket.getOutputStream());
String readline;
readline=sin.readLine();
while(!readline.equals("bye"))
{
os.println(readline);
os.flush();
System.out.println("Client:"+readline);
readline=sin.readLine();
}
os.close();
socket.close();
}
catch(Exception e)
{
System.out.println("Error"+e);
}
}
}
服务器端:
import java.io.*;
import java.net.*;
public class TalkServer
{
public static void main(String args[])
{
try
{
ServerSocket server=null;
server=new ServerSocket(4701);
Socket socket=null;
socket=server.accept();
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String clientString=is.readLine();
System.out.println("Client:"+clientString);
while(!clientString.equals("bye"))
{
clientString=is.readLine();
System.out.println("Client:"+clientString);
}
is.close();
socket.close();
server.close();
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
}
}
一个简单的网络通信程序,将客户机的信息发送给服务器,服务器予以回应
客户端:
import java.io.*;
import java.net.*;
public class TalkClient
{
public static void main(String args[])
{
try
{
Socket socket=new Socket("127.0.0.1",4701);
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
PrintWriter os=new PrintWriter(socket.getOutputStream());
BufferedReader is=new BufferedReader( new InputStreamReader(socket.getInputStream()));
String readline;
readline=sin.readLine();
os.println(readline);
os.flush();
System.out.println("Client:"+readline);
System.out.println("Server:"+is.readLine());
os.close();
is.close();
socket.close();
}
catch(Exception e)
{
System.out.println("Error"+e);
}
}
}
服务器端:
import java.io.*;
import java.net.*;
public class TalkServer
{
public static void main(String args[])
{
try
{
ServerSocket server=null;
server=new ServerSocket(4701);
Socket socket=null;
socket=server.accept();
String readline;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter os=new PrintWriter(socket.getOutputStream());
String clientString=is.readLine();
System.out.println("Client:"+clientString);
os.println(clientString.toUpperCase());
os.close();
is.close();
socket.close();
server.close();
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
}
}
一个网络通信程序,双方互相轮流通信:
客户端:
import java.io.*;
import java.net.*;
public class TalkClient
{
public static void main(String args[])
{
try
{
Socket socket=new Socket("127.0.0.1",4701);
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
PrintWriter os=new PrintWriter(socket.getOutputStream());
BufferedReader is=new BufferedReader( new InputStreamReader(socket.getInputStream()));
String readline;
readline=sin.readLine();
while(!readline.equals("bye"))
{
os.println(readline);
os.flush();
System.out.println("Client:"+readline);
System.out.println("Server:"+is.readLine());
readline=sin.readLine();
}
os.close();
is.close();
socket.close();
}
catch(Exception e)
{
System.out.println("Error"+e);
}
}
}
服务器端:
import java.io.*;
import java.net.*;
public class TalkServer
{
public static void main(String args[])
{
try
{
ServerSocket server=null;
try
{
server=new ServerSocket(4701);
}
catch(Exception e)
{
System.out.println("can not listen t"+e);
}
Socket socket=null;
try
{
socket=server.accept();
}
catch(Exception e)
{
System.out.println("Error."+e);
}
String readline;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter os=new PrintWriter(socket.getOutputStream());
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
System.out.println("Client:"+is.readLine());
readline=sin.readLine();
while(!readline.equals("bye"))
{
os.println(readline);
os.flush();
System.out.println("Server:"+readline);
System.out.println("Client:"+is.readLine());
readline=sin.readLine();
}
os.close();
is.close();
socket.close();
server.close();
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
}
}
一个网络通信程序,双方互相通信,并且无需轮流进行(程序主线程实现向网络的信息输出,启动新的线程实现读取从网络输入的信息):
客户机端:
import java.io.*;
import java.net.*;
public class ClientDemo
{
public static void main(String[] args) throws IOException
{
Socket kkSocket = null; //创建Socket类
PrintWriter out = null; //创建文件写出器
BufferedReader in = null; //创建文件读入器,主要应用该类的readLine()方法
boolean runable = true; //与服务器通话的状态
String toServer; //写到服务端的信息
try
{
//通过8888Port使客户端接口与服务器接口联系
kkSocket = new Socket("localhost", 8888);
//分别建立接口的读入与写出器
out = new PrintWriter(kkSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
}
catch(UnknownHostException e)
{
System.err.println("找不到服务器");
System.exit(1);
}
catch (IOException e)
{
System.err.println("不能获得Socket的读入与写出器");
System.exit(1);
}
//通过键盘输入器建立另一个文件读入器
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
//向服务器输入信息
out.println("新的用户登陆");
//建立显示服务端信息的线程
ReadServerThread readServerThread = new ReadServerThread(in);
readServerThread.start(); //启动线程
//进入与服务器对话的循环
while (runable)
{
//向服务器输入信息
toServer = stdIn.readLine();
out.println(toServer);
//当输入的信息是"Bye."时,退出程序
if (toServer.equals("Bye.")) break;
runable = readServerThread.runable; //获取线程的运行状态
}
readServerThread.fromServer = "欢迎下次再来.";
readServerThread.runable = false;//关闭线程
out.close(); //关闭文件写出器
in.close(); //关闭文件读入器
stdIn.close(); //关闭文件读入器
kkSocket.close(); //关闭Socket类
}
}
class ReadServerThread extends Thread
{
BufferedReader in = null;
String fromServer = "";
boolean runable = true;
public ReadServerThread(BufferedReader in)
{
this.in = in;
}
public void run()
{
while(runable)
{
//显示来自服务器的信息
try
{
fromServer = in.readLine();
}
catch(Exception e)
{
runable = false;
}
//当服务端输入Bye.时,客户端结束显示服务端信息与向服务端写入信息的两个循环
if(fromServer.equals("Bye"))
{
System.out.print("服务器程序退出");
runable = false; //结束向服务端写入信息循环
break; //结束显示服务端信息循环
}
System.out.println("服务器: " + fromServer);
}
}
}
服务器端:
import java.net.*;
import java.io.*;
public class ServerDemo
{
public static void main(String[] args) throws IOException
{
String toClient; //写到客户端的信息
PrintWriter out = null; //创建文件写出器
BufferedReader in = null; //创建文件读入器
boolean runable = true; //与客户端通话的状态
//创建SeverSocket的服务接口
ServerSocket serverSocket = null;
try
{
//使该接口应用端口1111
serverSocket = new ServerSocket(8888);
}
catch(IOException e)
{
System.err.println("不能创建1111端口");
System.exit(1);
}
//创建Socket的客户接口,当有客户端的程序访问该服务接口时激活该类
Socket clientSocket = null;
try
{
//通过accept()方法使服务器与客户端的Socket接口建立联系
clientSocket = serverSocket.accept();
}
catch(IOException e)
{
System.err.println("访问端口失败");
System.exit(1);
}
//分别创建客户端的Socket接口的读入与写出器
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//通过键盘输入器建立另一个文件读入器
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
//当有客户端的程序进入时显示欢迎信息
toClient = "您好,欢迎您!";
out.println(toClient);
//建立显示客户端信息的线程
ReadClientThread readClientThread = new ReadClientThread(in);
readClientThread.start(); //启动线程
//进入与客户端对话程序
while (runable)
{
//向客户端写出服务端的键入信息
toClient = stdIn.readLine();
out.println(toClient);
//如果服务端写入"Bye",退出对话程序
if (toClient.equals("Bye")) break;
runable = readClientThread.runable; //获取线程的运行状态
}
readClientThread.fromClient = "欢迎下次再来.";
readClientThread.runable = false;//关闭线程
out.close(); //关闭文件写出器
in.close(); //关闭文件读入器
stdIn.close(); //关闭文件读入器
clientSocket.close(); //关闭客户端接口
serverSocket.close(); //关闭服务端接口
}
}
class ReadClientThread extends Thread
{
BufferedReader in = null;
String fromClient = "";
boolean runable = true;
public ReadClientThread(BufferedReader in)
{
this.in = in;
}
public void run()
{
while(runable)
{
//显示客户端的信息
try
{
fromClient = in.readLine();
}
catch(Exception e)
{
runable = false;
}
//当客户端输入Bye.时,服务端结束显示客户端信息与向客户端写入信息的两个循环
if(fromClient.equals("Bye."))
{
System.out.print("客户端程序退出");
runable = false; //结束向客户写入信息循环
break; //结束显示客户端信息循环
}
System.out.println("客户端:" + fromClient);
}
}
}
说明:
如果要实现多客户机共同访问一台服务器就要考虑多线程的处理,必要时还要使用线程池提高性能
典型代码为:
while(true)
{
socket=serversocket.accept();
new Thread()...
}
3 Web相关类
3、1 URL类
URL是指统一资源定位符,由四个部分组成,分别为协议、IP地址(主机名)、端口号和文件路径(可以是实际路径,也可以是虚拟路径)
此类主要用于获取URL的相关信息,如
import java.io.*;
import java.net.*;
public class exec
{
public static void main (String [] args) throws IOException
{
URL url=new URL("http://localhost:80/postinfo.html");
System.out.println(url.getHost());
System.out.println(url.getProtocol());
System.out.println(url.getPort());
System.out.println(url.getFile());
System.out.println(url);
}
}
也可以利用URL获取文件流来读取网页信息,如:
import java.io.*;
import java.net.*;
public class url
{
public static void main(String args[])
{
FileOutputStream fos;
URL url;
InputStream is;
int i;
try
{
fos = new FileOutputStream("postinfo.html");
url = new URL("http://localhost/postinfo.html");
System.out.println(url.getFile());
is = url.openStream();
i = is.read();
while(i > 0)
{
fos.write(i);
i = is.read();
}
fos.close();
is.close();
}
catch (IOException e)
{
}
}
}
也可以写成:
import java.io.*;
import java.net.*;
public class url
{
public static void main(String args[])
{
FileOutputStream fos;
URL url;
InputStream is;
String str="";
try
{
fos = new FileOutputStream("postinfo.html");
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos,"gb2312"));
url = new URL("http://localhost:80/postinfo.html");
//System.out.println(url.getFile());
is = url.openStream();
BufferedReader br =new BufferedReader(new InputStreamReader(is,"gb2312"));
str=br.readLine();
while(str!=null)
{
bw.write(str+"\n");
str=br.readLine();
}
bw.flush();
fos.close();
is.close();
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
}
}
3、2 URLConnection类
它是个抽象类,封装应用程序到URL对象的连接,可以通过它获取网页的相关属性信息。
import java.net.*;
import java.util.*;
public class urlconnection
{
public static void main(String args[])
{
URL url;
URLConnection urlConnection;
try
{
url = new URL("http://localhost:80/postinfo.html");
urlConnection = url.openConnection();
System.out.println(urlConnection.getContentType());
System.out.println(new Date(urlConnection.getLastModified()));
System.out.println(urlConnection.getContentLength());
}
catch (Exception e)
{
}
}
}
也可以使用它获取网页流
import java.io.*;
import java.net.*;
public class urlconnection
{
public static void main(String args[])
{
FileOutputStream fos;
URL url;
URLConnection urlConnection;
InputStream is;
int i;
try
{
fos = new FileOutputStream("postinfo.html");
url = new URL("http://localhost:80/postinfo.html");
urlConnection = url.openConnection();
is = urlConnection.getInputStream();
i = is.read();
while(i > 0)
{
fos.write(i);
i = is.read();
}
fos.close();
is.close();
}
catch (IOException e)
{
}
}
}
3、3 URLEncoder类
用于转换字符串为CGI程序所理解的格式。
import java.net.*;
public class urlencoder
{
public static void main(String args[])throws MalformedURLException,java.io.UnsupportedEncodingException
{
System.out.println(URLEncoder.encode("Mrs & Mr John Abraham","UTF-8"));
System.out.println(URLEncoder.encode("南京","UTF-8"));
}
}
[此贴子已经被作者于2010-12-12 08:04:32编辑过]