1 TCP Sockets基础
Sockets是一个编程抽象概念,它是网络上与另一个应用程序通信连接的句柄。Sockets编程将用户代码与TCP/IP协议堆栈的底层实现隔离开,允许用户灵活地实现自己的编程。
基于TCP协议的TCP Sockets需要四个方面的数据:
(1) 本地系统的IP地址。
(2) 本地应用程序正在使用的TCP端口号。
(3) 通信的远程系统的IP地址。
(4) 通信的远程系统响应的TCP端口号。
TCP Sockets的应用模型通常是客户/服务器模型,一个服务器在一个特定端口等待远程计算机请求资源,给予响应。客户程序绑定到这个端口,建立一个可靠连接来用于通信。
面向连接的操作使用TCP协议,在这个模式下的Socket必须在发送数据之前与目的地的Socket取得一个连接。连接建立后,Socket就可以使用一个流接口:打开—读—写—关闭。所有发送
信息都会在另一端以同样的顺序被接收。面向连接的操作比无连接的操作效率低,但是数据的安全性更高。由于Socket使用是双方的,所以在客户端和服务器端的使用稍有不同。
(1) 客户端请求Socket的步骤如下:
① 建立客户端Socket连接;
② 得到Socket的读和写的流;
③ 利用流;
④ 关闭流;
⑤ 关闭Socket。
使用一个服务器端的Socket请求比使用一个客户端Socket请求要麻烦一些。服务器并不是主动地建立连接。相反地,服务器被动地监听客户端的连接请示,然后给它们服务。
(2) 服务器端Socket要完成以下的基本的步骤:
① 建立一个服务器Socket并开始监听;
② 使用accept()方法取得新的连接;
③ 建立输入和输出流;
④ 在已有的协议上产生会话;
⑤ 关闭客户端流和Socket;
⑥ 回到②或者转到⑦;
⑦ 关闭服务器Socket。
1.1 InetAddress类
InetAddress对象表示一个Internet主机地址。这个主机地址可以通过名字和IP地址来标识。名字即主机名,例如本机名字为snowing,为字符串类型;IP地址为192.100.100.43,为四字节的数字,书写形式为a.b.c.d。InetAddress类的几个常用方法如下:
public static InetAddress getByName(String host) throws UnknownHostException 通过名字可以得到主机的IP地址。
public String getHostAddress() 返回IP地址的字符串格式。
public String getHostName() 返回IP地址的主机名。
public static InetAddress getLocalHost() throws UnknownHostException 以InetAddress类封装的格式返回本机地址。
public String toString() 转换成字符串格式。
例:InetAddress对象应用的测试。获取本机地址并转换成字符串输出,输出本地主机名和IP地址。
import java.net.*;
public class FindHost
{
public static void main(String[] args)
{
try
{
InetAddress h = InetAddress.getLocalHost();
System.out.println("toString():" + h.toString());
System.out.println("getHostName():" +h.getHostName());
System.out.println("getHostAddress():"+h.getHostAddress());
}
catch(UnknownHostException e)
{
System.out.println(e.getMessage());
}
}
}
1.2 Socket类
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而
已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。
Socket类用来实现客户端的Socket。常用的构造方法有以下三种:
public Socket() 创建一个无连接的Socket。
public Socket(InetAddress address, int port) 创建流式Socket,将它连接到InetdAdress类指明的主机和port端口上。
public Socket(String host, int port) 创建流式Socket并将它连接到特定host的特定port端口上。
1.2.1 建立客户端程序,访问网址10.167.10.133,返回网址的首页写入文件jftt.html。
1. 程序建立的步骤
(1) 建立到 10.167.10.133且端口为80的Socket连接。
Socket clientSocket = new Socket("10.167.10.133", 80);
(2) 初始化字节流。连接建立后的数据传输通过数据输入输出流实现,写文件又通过文件输入输出流来实现。各种流对象的初始化如下:
DataOutputStream outbound = new DataOutputStream(clientSocket.getOutputStream());
DataInputStream inbound = new DataInputStream(clientSocket.getInputStream());
InputStreamReader inS = new InputStreamReader(inbound);
File f = new File("jftt.html");
FileOutputStream fOut = new FileOutputStream(f);
PrintStream p = new PrintStream(fOut);
(3) 发送请求。
outbound.writeBytes("GET / HTTP/1.0\r\n\r\n");
(4) 返回数据后,循环写入文件。
int c;
while((c = inS.read()) != -1)
p.print((char)c);
(5) 关闭流。
inS.close();
outbound.close();
inbound.close();
clientSocket.close();
1.3 Socket客户端与服务器端通信
1.3.1 Socket 客户端实例
如下的GreetingClient 是一个客户端程序,该程序通过socket连接到服务器并发送一个请求,然后等待一个响应。
package scoket; import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; public class GreetingClient { public static void main(String[] args) {
// TODO 自动生成的方法存根
String serverName = args[1];
int port = Integer.parseInt(args[0]); 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();
}
}
}
1.3.2 Socket 服务端实例
如下的GreetingServer 程序是一个服务器端应用程序,使用Socket来监听一个指定的端口。
package scoket; import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException; public class GreetingServer extends Thread{ // TODO 自动生成的方法存根
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();
}
} }
1.3.3 运行.java
1)首先在调试配置里配置运行参数args[0]、args[1],即:6066 localhost
2)编译运行GreetingServer,控制台输出:Waiting for client on port 6066...
3)编译运行GreetingClient,控制台输出: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!
2 网页显示控件
2.1 JEditorPane
JEditorPane类是一个文本组件,通过实现相应的EditorKit接口可以编辑一些特殊格式的内容。它的内容类型有三种:
(1) text/plain:使用默认的DefaultEditorKit的扩展,处理普通文本。
(2) txt/html:使用javax.swing.text.html.HTMLEditorKit提供HTML3.2的帮助,用以处理HTML文本,针对HTML中存在的一些链接,进行点击时可以激活超链接事件。
(3) txt/rtf:使用javax.swing.text.rtf.RTFEditorKit实现对RTF格式文本的处理。
这里侧重于处理HTML文本的情况,JEditorPane类的构造函数和处理HTML常用到的方法如下:
public JEditorPane() 构建一个空JeditorPane。
public JEditorPane(String url) 构建一个基于URL声明的字符串的JEditorPane。
public JEditorPane(URL initialPage) 构建一个基于特定URL对象的JeditorPane。
public void addHyperlinkListener(HyperlinkListener listener) 添加一个超链接监听者,通知任何变化,例如选中一个链接并进入时。
public void setPage(String url) 设置显示的URL页面。 字符串url 表示页面的URL地址。
public void setPage(URL page) 设置显示的URL对象代表的page页面。
public void setText(String t) 设置指定t内容。
public void setEditable(boolean b) b为true时,可编辑;b为false时,不可编辑,但只有当b为false时,才可以响应超链接事件。
2.2 HyperlinkListener和HyperlinkEvent
HyperlinkListener是超链接事件监听者接口,继承于EventListener接口,只有一个事件:超链接更新,当点击链接或者进入链接时引发。这个事件的方法为:
public void hyperlinkUpdate(HyperlinkEvent e) 当更新一个超链接时调用此方法。
HyperlinkEvent是超链接事件,用于通知有关超文本链接的变化,它有两个常用方法,一个方法用来获得事件的类型,另一个方法是获得超链接指向的URL网址。
public HyperlinkEvent.EventType getEventType()
HyperlinkEvent.EventType
HyperlinkEvent.ACTIVATED 激活超链接HyperlinkEvent.ENTERED 进入超链接的文本
HyperlinkEvent.EXITED 退出超链接的文本
public URL getURL() 返回超链接指向的URL地址
例:使用JEditorPane显示网页,当单击其中的链接时能够给予响应。
1. 分析
(1) 创建JEditorPane对象。
JEditorPane jep = new JEditorPane();
(2) 设置可编辑属性为false。
jep.setEditable(false);
(3) 添加超链接事件监听器。
jep.addHyperlinkListener(new LinkFollower(jep));
(4) 显示网页。
jep.setPage(str);
(5) 链接更新事件的处理。
public void hyperlinkUpdate(HyperlinkEvent evt)
{
if(evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
jep.setPage(evt.getURL());
}
例:显示本公司的网页
package scoket;
import java.io.*; import javax.swing.*;
import javax.swing.event.*; public class DisplayHtml { //在新窗口显示网页
@SuppressWarnings("deprecation")
public static void showNetPage(String str)
{
JEditorPane jep = new JEditorPane();
jep.setEditable(false);
jep.addHyperlinkListener(new LinkFollower(jep));
try
{
jep.setPage(str);
}
catch(IOException e)
{
jep.setContentType("text/html");
jep.setText("<html>Could not load "+ str + " </html>");
}
JScrollPane jscrollp = new JScrollPane(jep);
JFrame f = new JFrame("富士通主页");
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
f.setContentPane(jscrollp);
f.setSize(600, 400);
f.show();
}
public static void main(String[] args)
{
String getURL = "http://10.167.10.133/";
showNetPage(getURL);
}
}
class LinkFollower implements HyperlinkListener
{
private JEditorPane jep;
public LinkFollower(JEditorPane jep)
{
this.jep = jep;
}
//超链接更新事件的处理
public void hyperlinkUpdate(HyperlinkEvent evt)
{
//判断是否是激活事件
if(evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
{
try
{
//显示新的网址
jep.setPage(evt.getURL());
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
}
}
3 URL类
URL类代表统一资源定位符(Uniform Resource Locator),指向WWW上的资源,资源可以是一个文件或者一个目录,也可以是一个更加复杂的对象,如对数据库的查询或者搜索引擎的查询。通常,一个URL可以被拆分成几个部分,例如:
http://192.100.100.43:8080/web/index.html?search="Java "
其中: (1) http用来描述使用的协议为超文本链接协议。
(2) 192.100.100.43是信息存放的主机IP地址(也可以是主机名,如www.sohu.com.cn)。
(3) 端口号为8080,默认都为80端口。
(4) web/index.html为HTTP服务器上文件的虚拟路径。
(5) ?search="Java"为网页上表单使用GET方法进行查询的附加部分,?表示后面跟的是提交的表单的名-值对,格式为“名=值”。search为查询词的名字,Java为查询词的值,即用户要查询的值,提交搜索引擎时可以有0个到多个这样的查询名-值对。
URL类用来定位WWW上的资源,从而进行处理,如读取网页等。它的构造函数以及一系列常用的方法如下:
public URL(String spec) 从指定的字符串spec创建一个URL对象。
public URL(String protocol, String host, int port, String file) 从指定的protocol协议、host主机、port端口号和file文件名创建一个URL对象。
public URL(String protocol, String host, String file) 从指定的protocol协议、主机名host和文件名file创建一个URL对象。
public Object getContent() 得到URL的内容。
public String getContentType() 返回网页内容类型,普通网页为“text/html”。
public String getFile() 得到URL文件名。
public String getHost() 得到URL的主机名。
public String getPath() 得到URL的路径部分。
public int getPort() 得到URL的端口号。
public String getProtocol() 得到URL的协议名。
public String getQuery() 得到URL的查询部分。
public URLConnection openConnection() 返回一个URLConnection对象,代表到URL远程对象的连接。
public InputStream openStream() 打开到URL的连接,返回读取连接的输入流。
public String toExternalForm() 构建代表URL对象的字符串。
public String toString() 转换成字符串,覆盖来自对象的toString()方法。
4 URLEncoder类
URLEncoder类是一个工具类,用于HTML表单的编码。类只包含一个静态方法,这个方法将字符串转换成application/x-www-form-urlencoded MIME格式。该方法如下:
public static String encode(String s, String enc) throws UnsupportedEncodingException
使用指定的enc编码格式将字符串s转换为application/x-www-form-urlencoded格式,得以在网上传输。
其中:
(1) s为要转换的字符串。
(2) enc是字符编码格式名称,包括US-ASCII、ISO-8859-1、UTF-8等。
例:将查询表单提交的网址和相应的两个查询“名-值对”使用“UTF-8”编码格式进行编码。
1. 分析
(1) 在程序中定义了一个QueryString类,实现多个名-值对的编码和连接。其中一对名-值对编码如下:
query = URLEncoder.encode(name.toString(),"UTF-8") + "=" + URLEncoder.encode(value.toString(),"UTF-8");
(2) 名-值对之间用符号&连接。
if(!query.trim().equals(""))
query += "&";
(3) 主函数中对QueryString类的引用。
QueryString q = new QueryString("cdtype","GB");
q.add("word","Java");
2. 源程序
package uRL;
import java.net.*;
import java.io.*; public class UseEncode {
public static void main(String[] args)
{
String fullURL = "http://bingle.pku.edu.cn/scripts/ftp_search.exe?";
//新建QueryString对象,调用方法
QueryString q = new QueryString("cdtype","GB");
q.add("word","Java");
fullURL += q.toString();
//打印编码后的字符串
System.out.println("编码后的字符串:" + fullURL);
}
}
//类:处理请求串,编码成网页识别格式
class QueryString
{
private String query;
//构造函数,初始名值对的编码
public QueryString(Object name, Object value)
{
try
{
query = URLEncoder.encode(name.toString(),"UTF-8") + "=" +
URLEncoder.encode(value.toString(),"UTF-8");
}
catch(UnsupportedEncodingException e)
{
System.err.println(e);
}
}
//构造函数 //添加名值对,之间用符号&进行连接
public synchronized void add(Object name, Object value)
{
if(!query.trim().equals(""))//如果字符串为空,则与""相等
query += "&";
try
{
query += URLEncoder.encode(name.toString(),"UTF-8") + "=" +
URLEncoder.encode(value.toString(),"UTF-8");
}
catch(UnsupportedEncodingException e)
{
System.err.println(e);
}
}
//返回编码后的字符串
public String toString()
{
return query;
}
//清空
public void clear()
{
query = "";
}
}
5 URLDecoder类
URLDecoder类是URLEncoder类的解码类。它的构造函数和解码方法如下:
public URLEncoder()
public static String decode(String s, String enc)
使用编码格式enc对application/x-www-form-urlencoded字符串s进行解码。
6 URLConnection类
抽象类URLConnection代表应用程序和URL之间通信连接的类的超类。类的实例可以用来读取和写入URL代表的资源。URLConnection类实现的两个方法。
openConnection() 处理影响到远程资源的连接参数
connect() 同资源进行交互,查询头文件和内容
上述两个方法的实现步骤如下:
(1) 调用openConnection方法建立连接对象。
(2) 操作建立参数和普通请求参数。
(3) 调用connect方法建立到远程对象的实际连接。
(4) 远程对象可用,头文件和远程对象的内容可以访问。