Java网络编程技术.docx
《Java网络编程技术.docx》由会员分享,可在线阅读,更多相关《Java网络编程技术.docx(48页珍藏版)》请在冰点文库上搜索。
Java网络编程技术
第14章Java网络编程技术
网络编程的目的就是直接或间接地通过网络协议与其他计算机进行通信。
网络编程中有两个主要的问题:
一个是如何准确地定位网络上一台或多台主机,另一个就是找到主机后如何可靠、高效地进行数据传输。
在TCP/IP协议集中,IP协议主要负责网络主机的定位,由IP地址可以唯一地确定Internet上的一台主机;而TCP协议实现可靠的数据传输。
目前较为流行的网络编程模型是客户机/服务器(C/S)模式,即通信的一方作为服务器等待客户机提出请求并予以响应;客户机则在需要服务时向服务器提出申请。
服务器一般作为守护进程始终运行,监听服务器的网络端口,一旦某个客户机提出请求,服务器就会为每个客户机创建并启动一个服务线程来响应该客户机,同时自己继续监听服务器的网络端口,使后来的客户机也能及时得到服务。
14.1使用URL
URL(UniformResourceLocator,统一资源定位器)主要用来对Internet上的信息进行定位。
浏览器借助它来查找Web上的信息。
实际上,Web通过URL和HTML对所有的网络资源定位。
在Java的网络编程中,程序员使用URL类库,获取Internet上的信息。
14.1.1URL组成
URL的两种语句格式:
1.
2.:
80/index.htm
从上面的URL格式可以看出,URL由以下4个部分组成:
(1)协议(http)。
指网络连接用到的协议,如,本例的http是超文本协议,http不是唯一的协议,常见的协议还有FTP、Gopher等。
其中的冒号:
将协议与URL的其他部分隔开。
(2)主机名或其IP地址()。
该项位于双斜线(//)和单斜线(/)之间,其中的冒号(:
)是可选部。
如,本例的/或:
80表示了主机名。
(3)端口号(80)。
它是可选的参数,它位于主机名和右边的单斜线(/)之间(HTTP协议的默认端口为80,所以“:
80”可以不写),可以是别的数字组成端口号。
(4)网页文件及所在的目录(index.htm)。
网页文件及目录是浏览器要查找的网页资源。
如index.html或index.htm文件。
14.1.2URL类
在Java中URL类的继承关系如下:
publicfinalclassURLextendsObjectimplementsSerializable
1.URL的构造方法
URL类有多个构造方法,它们都可能抛出MalformedURLException异常。
(1)URL(StringurlSpecifier)的使用格式
URLurl=newURL("
(2)URL(Stringprotocol,StringhostName,intport,Stringpath)的使用格式
URLurl=newURL("http","",80,"nba");
(3)URL(Stringprotocol,StringhostName,Stringpath)的使用格式
URLurl=newURL("http","","nba");
(4)URL(URLcontext,Stringspec)使用一个已经存在的URL创建一个新的URL
URLurl1=newURL("//构造url1对象
URLurl2=newURL(url,"nba");//利用上面的url1对象和一个字符串为参数创建新的url2对象
2.URL的实用方法
●booleanequals(Objectobj):
比较两个URL是否相同。
●StringgetAuthority():
获得URL的授权部分。
●ObjectgetContent():
获得URL的内容。
●ObjectgetContent(Class[]classes):
获得URL的内容。
●intgetDefaultPort():
获得URL中的默认端口号。
●StringgetFile():
获得URL中的网页文件名。
●StringgetHost():
获得URL中的主机名。
●StringgetPath():
获得URL中网页所在的路径。
●intgetPort():
获得URL中的端口号,若没有设置端口号则返回-1。
●StringgetProtocol():
获得URL所用的协议名称。
●StringgetQuery():
获得URL的查询部分。
●StringgetRef():
获得URL的锚点(也称为“引用”)。
●StringgetUserInfo():
获得URL的UserInfo部分。
●inthashCode():
创建一个适合散列表索引的整数。
●InputStreamopenStream():
打开URL的链接。
并返回一个用于从该链接读入的InputStream流。
●booleansameFile(URLother):
比较两个URL,不包括片段部分。
●StringtoExternalForm():
将URL转换为字符串格式。
●StringtoString():
构造URL的字符串表示形式。
●URItoURI():
返回与URL等效的URI。
3.URL应用
【例14.1】创建一个URL实例,然后检查它的相关属性。
程序名:
Example14_1.java。
【程序源代码】
import.*;
classExample14_1{
publicstaticvoidmain(Stringargs[])throwsMalformedURLException{
try{
URLurl=newURL("");//创建一个URL对象
System.out.println("授权:
"+url.getAuthority());//检查它的授权部分
System.out.println("协议名称:
"+url.getProtocol());//获得此URL的协议名
System.out.println("默认端口号:
"+url.getDefaultPort());//获得默认端口号
System.out.println("主机名:
"+url.getHost());//获得此URL的主机名
System.out.println("文件名:
"+url.getFile());//获得此URL的文件名
System.out.println("Ext:
"+url.toExternalForm());
System.out.println("字符串表示形式:
"+url.toString());
}
catch(MalformedURLExceptionex){System.out.println("fail!
");}
}
}
【例14.2】在Applet的文本框中输入一个网址,单击search(搜索)按钮后链接到该网址相对应的页面。
程序名:
Example14_2.java
【程序说明】要在Applet中链接到其它Web页面,可使用下面的代码来实现。
getAppletContext().showDocument(url);//执行该方法,转向url页面
因为getAppletContext()方法是在Applet类中定义的,由showDocument()定位到另一个Web页面。
【程序源代码】
importjava.awt.*;importjava.awt.event.*;importjavax.swing.*;
import.*;importjava.io.*;importjava.applet.*;
publicclassShowWebPageextendsJAppletimplementsActionListener{
privatestaticfinallongserialVersionUID=1L;
privateJLabellfn=newJLabel("URLaddress");
privateJTextFieldtfu=newJTextField(20);//网址输入框
privateJButtonsearb=newJButton("search");//搜索按钮
/**初始化界面*/
publicvoidinit(){
Containerc=getContentPane();//获取底层容器
searb.addActionListener(this);//为按钮注册监听器
c.setLayout(newFlowLayout());//设置布局
c.add(lfn);//添加标签
c.add(tfu);//添加网址输入文本框
c.add(searb);//添加Search(搜索)按钮
}
/**响应按钮事件*/
publicvoidactionPerformed(ActionEvente){
Strings=tfu.getText();//获取输入框内容
try{
URLu=newURL(s);//利用网址输入框中的内容创建一个URL对象
getAppletContext().showDocument(u);//显示指定URL的网页
}
catch(MalformedURLExceptionex){showStatus(s+"fileformaterror!
");}
}
}
14.2Socket套接字
14.2.1Socket的含义
Socket描述了网络上运行的两个程序间实现通信的任一端。
它既可以接收请求,也可以发送请求。
利用它可以方便地编写网络上传递数据的程序。
在Java中,有专门的Socket类来处理用户的请求和响应。
利用Socket类的方法,就可以实现两台计算机之间的通信。
在Java中,Socket可以理解为客户端或者服务器端的一个特殊的对象。
这个对象有两个关键的方法:
一个是getInputStream()方法;另一个是getOutputStream()方法。
getInputStream()方法可以得到一个输入流。
客户端的Socket对象上的getInputStream()方法得到的输入流就是从服务器端发送回来的数据流。
getOutputStream()方法得到一个输出流。
客户端的Socket对象上的getOutputStream()方法返回的输出流就是将要发送到服务器端的数据流(其实是一个缓冲区,暂时存储将要发送到服务器端的数据)。
下面介绍Socket实现网络通信的机制。
先来看看服务器是如何与客户机进行通信的。
每当客户机要求与服务器通信时,在服务器端,服务器套接字便会为客户机创建一个客户机的代理套接字。
通过代理套接字,服务器与客户端的客户机套接字建立一个连接。
通过这个连接,服务器便可与客户机进行通信。
其通信方式如图14-1所示。
图14-1服务器与客户机进行通信的方式
1.建立服务器端套接字
Java中有一个ServerSocket类,专门用来建立服务器端的套接字。
可以用服务器需要使用的端口号作为参数来创建服务器套接字。
ServerSocketserver=newServerSocket(8000);
这条语句创建了一个服务器套接字server。
该服务器使用8000号端口监听客户机请求。
2.在服务器端为每个客户机建立代理套接字
当一个客户端程序要求与服务器建立一个Socket连接时,在服务器端,服务器套接字便会为客户机建立一个客户机代理套接字connectToClient,通过该套接字与客户机端的Socket套接字建立连接。
(1)在服务器端为客户机建立代理套接字的格式如下:
SocketconnectToClient=server.accept();//代理套接字connectToClient
(2)服务器端从客户端收到的输入流s_in(也就是客户端的输出流)的其格式如下:
BufferedReaders_in=newBufferedReader(newInputStreamReader(connectToClient.getInputStream()));
(3)服务器端传给客户端的输出流s_out(也就是客户端的输入流)的格式如下:
PrintWriterout=newPrintWriter(connectToClient.getOutputStream(),true);
随后,就可以使用s_in.readLine()方法得到客户端的输入;也可以使用s_out.println()方法向客户端发送数据。
在所有通信结束以后应该关闭这两个数据流。
关闭的顺序是先关闭输出流,再关闭输入流,即依次调用out.close();和in.close();方法。
3.建立客户端套接字(Socket)
客户端只需用服务器所在机器的IP以及服务器的端口号作为参数即可创建一个客户端套接字(connectToServer)。
假设服务器的端口号是8000。
(1)建立客户机的套接字格式如下:
SocketconnectToServer=newSocket("服务器IP地址",8000);
或者
SocketconnectToServer=newSocket("服务器主机名",8000);
(2)客户端从服务器端收到的输入流in(也就是服务器端的输出流)的格式如下:
InputStreamin=connectToServer.getInputStream();
(3)客户端向服务器端发送的输出流out(也就是服务器端的输入流)的格式如下:
OutputStreamout=connectToServer.getOutputStream();
14.2.2Socket的应用
【例14.3】实现客户机和服务器相互交替接收对方所写入的信息,实现两者间的通信。
程序名:
MyClient.java&&MyServer.java
【程序源代码】
/**客户端程序:
MyClient.Java*/
packagesocket;import.*;importjava.io.*;
publicclassMyClient{
staticSocketserver;
publicstaticvoidmain(String[]args)throwsIOException{
try{
/**向本机的5678端口发出客户机请求
*由Socket对象得到输入流,并构造相应的BufferedReader对象
*由Socket对象得到输出流,并构造PrintWriter对象
*/
server=newSocket(InetAddress.getLocalHost(),5678);
BufferedReaderin=newBufferedReader(newInputStreamReader(server.getInputStream()));
PrintWriterout=newPrintWriter(server.getOutputStream());
/**由系统标准输入设备构造BufferedReader对象作为用户输入信息流*/
System.out.println("输入发送信息:
");
BufferedReaderwt=newBufferedReader(newInputStreamReader(System.in));
/**利用循环不断发送数据*/
while(true){
Stringstr=wt.readLine();//从系统标准输入设备读入一字符串
out.println(str);//将从系统标准输入设备读入的字符串输出到Server
out.flush();//刷新输出流,使Server马上收到该字符串
if(str.equals("end")){break;}
System.out.println("Server说:
"+in.readLine());//输出接收到信息
}
out.close();//关闭Socket输出流
in.close();//关闭Socket输入流
server.close();//关闭Socket
}
catch(Exceptione){System.out.println("error:
"+e);}
}
}
/**服务器端程序:
MyServer.java*/
packagesocket;//类包
importjava.io.*;import.*;
publicclassMyServer{
publicstaticvoidmain(String[]args)throwsIOException{
try{
//创建一个ServerSocket在端口5678监听客户机请求
ServerSocketserver=newServerSocket(5678);
/**使用accept()阻塞等待客户机请求,
*有客户机请求到来则产生一个Socket对象,并继续执行
*/
Socketclient=server.accept();
//显示连接到的客户机IP
System.out.println("connectto:
"+client.getInetAddress());
//由系统标准输入设备构造BufferedReader对象作为用户输入信息流
BufferedReaderwt=newBufferedReader(newInputStreamReader(System.in));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
BufferedReaderin=newBufferedReader(newInputStreamReader(client.getInputStream()));
//由Socket对象得到输出流,并构造PrintWriter对象
PrintWriterout=newPrintWriter(client.getOutputStream());
/**利用循环不断接收数据*/
while(true){
Stringstr1=in.readLine();
//在标准输出设备上打印从客户端读入的字符串
System.out.println("Client说:
"+str1);
if(str1.equals("end"))break;//若客户机输入"end",则断开与客户机的连接
Stringstr2=wt.readLine();
out.println(str2);//将从系统标准输入设备读入的字符串输出到客户机
out.flush();//刷新输出流,使服务器马上收到该字符串
}
out.close();//关闭Socket输出流
in.close();//关闭Socket输入流
client.close();//关闭Socket
server.close();//关闭ServerSocket
}
catch(Exceptione){System.out.println("error:
"+e);}
}
}
【程序运行结果】
将上面两个程序放在Socket目录下编译,先运行服务器端,然后运行客户端,结果如图14-2、图14-3所示。
图14-2例14.3客户端程序运行结果
图14-3例14.3服务器端程序运行结果
【例14.4】Socket的多线程通信。
程序名:
Cal_Client.java&&Cal_Server.java
【程序说明】在本例中,在客户机输入三角形3条边的长度并发送给服务器;服务器把计算出的三角形面积返回给客户机。
可以将计算量大的工作放在服务器端,客户端则负责计算量小的工作,实现客户机、服务器交互计算,从而完成任务。
套接字连接中涉及到输入流和输出流操作,为了不影响做其他的事情,应把套接字连接放在一个单独的线程中。
另外,服务器端为每个客户机创建并启动一个服务线程。
【程序源代码】
/**客户端代码:
Cal_Client.Java**/
import.*;importjava.io.*;importjava.awt.*;
importjava.awt.event.*;importjava.applet.*;importjavax.swing.*;
publicclassCal_ClientextendsJAppletimplementsRunnable,ActionListener
{
JButtonb_calc;//计算按钮
TextFieldb_edge,b_result;//b_edg是3条边输入的文本框;b_result是结果文本框
Socketsocket=null;//连接套接字
DataInputStreamin=null;//输入数据流
DataOutputStreamout=null;//输出数据流
Threadthread;//负责接收计算结果的线程
/**下面初始化界面*/
publicvoidinit()
{
setLayout(newGridLayout(2,2));
JPanelp1=newJPanel(),p2=newJPanel();
b_calc=newJButton("b_calc");
b_edge=newTextField(12);b_result=newTextField(12);
p1.add(newJLabel("输