一、 Java 适合网络编程的原因:
java 是一种平台无关的编程语言,具有“一次编写、到处运行”的特点,所以非常适合网络编程,并且在网络编程方面没有任何一门语言比 java 更优秀。
二、网络基础知识
1 、计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源。
2 、计算机网络是现代 通信技术 与 计算机技术 形结合的产物。
3 、计算机网络按 规模大小和延伸范围 分为 局域网( LAN ),城域网( MAN )和广域网( WAN ) ;按 网络拓扑结构 分为 环型网,星型网和总线型网 ;按 通信介质 分为 双绞线网、同轴电缆网、光纤网和卫星网 。
4 、计算机 网络协议 分为三部分: 语义 (决定双方对话类型)、 语法 (决定双方对话格式)和 变换规则 (决定通信双方应答关系)。
5 、网络体系结构: 国际标准化组织( ISO ) 于 l978 年 提出 “ 开放系统互连参考模型 ” ,即 OSI ( Open System Interconnection )模型 。该模型把计算机 网络分成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层等七层。
6 、 通信协议 是 计算机网络中实现通信必须有一些约定。对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
1 ) TCP/IP
TCP 协议 :是当今网络传输的基础协议,解决异构网络通信问题,提供可靠的数据传输服务的规则。
IP 协议 :规定了 Internet 数据传输的基本单元和格式,进行 IP 数据包的分割和组装
2 ) HTTP :超文本传输协议,用于在服务器和客户端浏览器见传输超文本格式信息的通信协议
3 ) FTP :文件传输协议,用于在服务器和客户机之间实现文件传输的通行协议。
4 ) SMTP :简单邮件传输协议,用于电子邮件的发送传输。
5 ) POP3 :邮局协议版本 3 ,接受 Internet 上电子邮件的第一个离线协议标准。
7 、 IP 地址 :为实现网络中不同的计算机之间的通信,在网络中的每台机器都必须有一个与众不同的标识 , 它可以唯一确定网络上的一个通信实体。
格式 : 数字型、 32 位、由 4 段 8 位的二进制数组成 。 一般表示为十进制形式( 4 个 0~255 的十进制整数),中间用圆点隔开 。
域名地址 :也是分段表示的,便于记忆的、字符串形式。
端口 :一个 16 位的整数 ,用于表示数据交给哪个通信程序处理。端口就是应用程序与外界交流的出入口,是一种抽象的软件结构,并且还包括一些数据结构和 I/O (基本输入 / 输出)缓冲区。
同一台机器上不能有两个程序使用同一个端口 , 端口号可以从 0 到 65535 ,通常将它分为三类:
公认端口 ( Well Known Ports ):从 0 到 1023 ,它们 紧密绑定( Binding )一些服务 。
注册端口 ( Registered Ports ):从 1024 到 49151 。它们 松散地绑定 一些服务。
动态和 / 或私有端口 ( Dynamic and/or Private Ports ):从 49152 到 65535 ,这些端口是应用程序使用的动态端口,应用程序一般不会主动使用这些端口。
三、 Java 的基本网络应用
1 、网络编程服务应用的包为 java.net 包。
2 、 InetAddress :用于描述 IP 地址的对象,有两个子类,为 Inet4Address 和 Inet6Address ,分别代表 IPv4 和 IPv6 。
3 、 InetAddress 没有提供构造方法 ,而是提供了 两个静态方法 来 获取 InetAddress 实例 :
1 ) getByName (String host) :根据 主机获取 对应的 InetAddress 对象。
2 ) getByAddress (byte[] addr) :根据 原始 IP 地址 来获取对应的 InetAddress 对象。 (字节数组) 提供了三个方法来获取实例对应 Ip 地址和主机名:
1 ) StringgetCanonicalHostName() : 获取次 IP 地址的完全限定域名
2 ) StringgetHostAddress(): 返回改 InetAddress 实例对应的 IP 地址(字符串形式)
3 ) String getHostName() : 获取此 IP 地址的主机名
例子说明 :
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpDemo {
/**
* @param args
* @throws UnknownHostException
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 根据主机获取对应的 InetAddress 对象 , 可以封装一个 IP 地址或网址
InetAddress ia= null ;
try {
ia = InetAddress.getByName( "192.168.49.1" );
// 获取 ia 的 IP 地址
System. out .println(ia.getHostAddress());
// 获取 ia 的主机名
System. out .println(ia.getHostName());
// 根据系统上配置的名称服务返回其 IP 地址所组成的数组
InetAddress[] ias=InetAddress.getAllByName( "www.baidu.com" );
for (InetAddress i:ias)
{
// 获取实例对应的 IP 地址和主机名
System. out .println(i.getHostAddress()+i.getHostName());
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
四、在 Java 中实现 UDP 协议编程
1 、 UDP( User DatagramProtocol ) 协议 是 用户数据报 ,在网络中它与 TCP 协议一样用于处理数据包。在 OSI 模型中,在第四层 —— 传输层,处于 IP 协议的上一层, TCP 层。
2 、 特点 : UDP 是一种 无连接的协议 ,每个数据报都是一个独立的信息,包括完整的源或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地, 到达目的地的时间以及内容的正确性都是不能被保证的。数据包是有限制的,不能大于 64k 。
3 、使用 UDP 原因:
在网络质量令人不十分满意的环境下, UDP 协议数据包丢失会比较严重。但是由于 UDP 的特性:它 不属于连接型协议 ,因而具有 资源消耗小 , 处理速度快 的优点,所以通常音频、视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
4 、 控制用户数据报文 : Java.net 包下的 DatagramSocket (套接字) 和 DatagramPacket 类
DatagramSocket 类 :创建 接收和发送 UDP 的 Socket 实例
1 ) DatagramSocket() :创建实例。通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。
2 ) DatagramSocket(int port) :创建实例,并固定监听 Port 端口的报文。
3 ) DatagramSocket(int port,InetAddress localAddr) :这是个非常有用的构建器,当一台机器拥有多于一个 IP 地址的时候,由它创建的实例仅仅接收来自 LocalAddr 的报文
注意 :在创建 DatagramSocket 类实例时,如果端口已经被使用,会产生一个 SocketException 的异常抛出,并导致程序非法终止,这个异常应该注意捕获。
方法:
1 ) receive(DatagramPacket d) :接收数据报文到 d 中。 receive 方法产生一个 “ 阻塞 ” 。
2 ) send(DatagramPacket d) :发送报文 d 到目的地。
3 ) setSoTimeout(int timeout) :设置超时时间,单位为毫秒。
4 ) close() :关闭 DatagramSocket 。在应用程序退出的时候,通常会主动释放资源,关闭 Socket ,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭 Socket ,或在捕获到异常抛出后关闭 Socket 。
“ 阻塞 ” 是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。
5 、 DatagramPacket :用于处理报文,将 byte 数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成 byte 数组。
1 ) DatagramPacket(byte[] buf, int length, InetAddress addr, intport) :从 buf 数组中,取出 length 长的数据创建数据包对象,目标是 addr 地址, port 端口。
2 ) DatagramPacket(byte[] buf, int offset, int length, InetAddressaddress, int port) :从 buf 数组中,取出 offset 开始的、 length 长的数据创建数据包对象,目标是 addr 地址, port 端口。
3 ) DatagramPacket(byte[] buf, int offset, int length) :将数据包中从 offset 开始、 length 长的数据装进 buf 数组。
4 ) DatagramPacket(byte[] buf, int length) :将数据包中 length 长的数据装进 buf 数组。
5 ) getData() :它从实例中取得报文的 byte 数组编码。
例子 : package com.hbsi.demo;
importjava.net.DatagramPacket;
importjava.net.DatagramSocket;
importjava.net.InetAddress;
publicclass SendDemo
{
/**
*@param args
*/
public static void main(String[] args)throws Exception
{
// TODO Auto-generated method stub
//1. 建立 udpsocket 服务
DatagramSocketds=new DatagramSocket();
//2. 将数据封装成数据包
//2.1 将要发送的数据封装成字节数组
byte[] buf="woshi shu ju".getBytes();
InetAddressis=InetAddress.getByName("192.168.49.1");
DatagramPacketdp=new DatagramPacket(buf, buf.length, is, 9009);
//3 发送
ds.send(dp);
//4 关闭
ds.close();
}
}
packagecom.hbsi.demo;
importjava.net.DatagramPacket;
importjava.net.DatagramSocket;
publicclass ReceiveDemo
{
/**
*@param args
*/
public static void main(String[] args)throws Exception
{
// TODO Auto-generated method stub
//1. 建立 udpsocket
DatagramSocketds=new DatagramSocket(9009);
//2. 建立一个数据包来接收数据
byte[] buf=newbyte[1024];
DatagramPacketdp=new DatagramPacket(buf,buf.length);
//3. 接收
ds.receive(dp);
//4. 通过数据包对象的方法获取数据
Stringip=dp.getAddress().getHostAddress();
String data=newString(dp.getData(),0,dp.getLength());
intport=dp.getPort();
System.out.println(ip+":"+data+":"+port);
//5 关闭
ds.close();
}
}
6 、发送端
1 )建立 udpsocket 服务端点。该端点建立,系统会随机分配一个端口。如果不想随机配置,可以手动指定: DatagramSocket ds = newDatagramSocket(9002);
2 )将数据进行 packet 包的封装,必须要指定目的地地址和端口。
byte[] buf = "wo shi shu ju".getBytes();
DatagramPacket dp =new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),9001);
3 )通过 socket 服务的 send 方法将该包发出: ds.send(dp);
4 )将 socket 服务关闭。主要是关闭资源: ds.close();
7 、接收端
1 )建立 udp 的 socket 服务 。要监听一个端口: DatagramSocket ds = newDatagramSocket(9001);
2 )定义一个缓冲区,将该缓冲区封装到 packet 包中。
byte [] buf = new byte [1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
3 )通过 socket 的 receive 方法将数据存入数据包中: ds.receive(dp);
4 )通过数据包 dp 的方法 getData() 、 getAddress() 、 getPort() 等方法获取包中的指定信息。
5 )关闭 socket : ds.close();
8 、 作业 :聊天程序有两个动作,一个接收一个发送。需要同时执行,就要使用到多线程技术。两个动作需要分别封装到两个 run 方法中。
packagecom.hbsi.demo;
importjava.io.BufferedReader;
importjava.io.InputStreamReader;
importjava.net.DatagramPacket;
importjava.net.DatagramSocket;
importjava.net.InetAddress;
/**
* 聊天对话,发送的时候同时也可以接受
*
*/
classChat1 implements Runnable{
@Override
public void run() {
try {
disPlay();
} catch (Exception e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
public static void disPlay()throwsException{
DatagramSocket ds=newDatagramSocket();
BufferedReader br=newBufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=br.readLine())!=null){
if(line.equals("886")){
break;
}
byte[] buf=line.getBytes();
InetAddressis=InetAddress.getByName("192.168.49.1");
DatagramPacket dp=newDatagramPacket(buf, buf.length, is,9009);
Strings=InetAddress.getLocalHost().toString();
//Strings1=InetAddress.getByName(s).toString();
System.out.print(s+":");
ds.send(dp);
}
}
}
classChat2 implements Runnable{
@Override
public void run() {
try {
disPlay();
} catch (Exception e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
public static void disPlay() throwsException{
DatagramSocket ds=newDatagramSocket(9009);
while(true){
byte[] buf=new byte[1024];
DatagramPacket dp=newDatagramPacket(buf, buf.length);
ds.receive(dp);
Stringhost=dp.getAddress().getHostAddress();
//int i=dp.getPort();
String data=newString(dp.getData()).trim();
System.out.println(host+" : "+data);
}
}
//ds.close();
}
publicclass SendReceiveDemo {
/**
*@param args
*/
public static void main(String[] args) {
Chat1 c1=new Chat1();
Chat2 c2=new Chat2();
Thread t1=new Thread(c1);
Thread t2=new Thread(c2);
t1.start();
t2.start()
}
}
基于 TCP 协议编程
应用的是客户 / 服务器模式
一、服务端:
ServerSocket : 用到 java.net.ServerSocket 类创建 服务器 Socket
构造方法:
1 ) ServerSocket(int port) :创建绑定到特定端口的服务器套接字
2 ) ServerSocket(int port, int backlog) :利用指定的 backlog( 服务器忙时保持连接请求的等待客户数量 ), 创建服务器套接字并将其绑定到指定的本地端口号。
3 ) ServerSocket(int port, int backlog,InetAddress bindAddr) :使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
创建一个 TCP 服务器端程序的步骤
(1) 创建一个 ServerSocket
(2) 从 ServerSocket 接受客户连接请求
(3) 创建一个服务线程处理新的连接
(4) 在服务线程中,从 socket 中获得 I/O 流
(5) 对 I/O 流进行读写操作,完成于客户的交互
(6) 关闭 I/O 流
(7) 关闭 Socket
代码说明:
SeverSocket server=new ServerSocket(port)
Socket s=server.accept();
ObjectInputStream in=newObjectInputStream(s.getInputStream());
ObjectOutputStreamout=newObjectOutputStream(s.getOutputStream());
out.close();
in.close();
s.close();
服务器端程序调用 ServerSocket 类中的 accept() 方法等待客户端的连接请求,一旦 accept() 接收了客户端连接请求,该方法返回一个与该客户端建立了专线连接的 Socket 对象,不用程序去创建这个 Socket 对象。建立了连接的两个 Socket 是以 IO 流的方式进行数据交换的, Java 提供了 Socket 类中的 getInputStream() 返回 Socket 的输入流对象, getOutputStream() 返回 Socket 的输出流对象。
二、客户端:
Socket : 客户端要与服务器建立连接 , 必须先创建一个 Socket 对象
构造方法 :
1)Socket(Stringhost, int port) :创建一个流套接字并将其连接到指定主机上的指定端口号。
2)Socket(InetAddressaddress, int port) :创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
创建一个 TCP 客户端程序的步骤
(1) 创建 Socket
(2) 获得 I/O 流
(3) 对 I/O 流进行读写操作
(4) 关闭 I/O 流
(5) 关闭 Socket
代码说明:
Socket connection=newSocket(127.0.0.1,7777);
ObjectInputStream input=newObjectInputStream(connection.getInputStream());
ObjectOutputStream output=newObjectOutputStream(connection.getOutputStream());
input.close();
output.close();
connection.close();
注意事项:
(1) 不要再服务程序中出现 static 属性,因为是所有客户端共享的。
(2) 不要从两个 Socket 中创建流用于通信,会出现通讯错误,原因:流的头不一样
(3) 不要从一个客户端多次创建 TCP 链接,那就成了两个客户端
(4) 要 控制链接数 ,否则资源可能不够用, 方法: public ServerSocket ( int port ,int backlog ) throwsIOException backlog: 指的是连接数
URL 类的使用
1 、在 java.net 包 中定义了 URL 类,该类用来处理有关 URL 的内容。
2 、 URL (Uniform Resource Locator) 统一资源定位器 是指互联网“资源”的名称。资源可以是简单的文件或目录,也可以是更为复杂的对象引用,例如对数据或搜索引擎的查询。通常 3 、 URL 可以由 协议名、主机、端口和资源 组成。
4 、 URL 的 格式 为:“ protocol : //host:port/resourceName ”。例如, URL 地址“ http://www.163.index.htm ”。
5 、 URL 类提供了多个构造方法创建 URL 对象,常用的有两个:
1 ) publicURL ( String str ) 使用指定的字符串构建。
2 ) Public URL ( URL context , Stringst r) 使用基地址和相对 URL 创建。
例如:可以通过绝对地址创建对象
URL url=new URL (” http://www.baidu.com ” ) ;
创建相对的 URL 对象。相对 UR L一般用在html文件中,是基于绝对 URL 的。例如:主 URL 是 http://www.baidu.com/ ,其下还有 URL---http://www.baidu.com/a.html, 以及 http://www.baidu.com/b.html, 则可以创建如下的 URL 对象。
URL url=newURL(“http://www.baidu.com”);
URL url=newURL(url“a.html”);
URL url=newURL(url“b.html”);
还有:
3 ) URL(Stringprotocol, String host, int port, String file) : 使用 指定的 协议、主机名、端口号、文件名创建。
4 ) URL(String protocol, String host, String file) : 使用 指定的 协议、主机名、文件名创建。
6 、获得 URL 对象之后,就可以调用如下方法来访问该 URL 对应的资源。
1 ) String getFile(): 获取此 URL 的资源名
2 ) String getHost(): 获取此 URL 的主机名
3 ) String getPath(): 获取此 URL 的路径部分
4 ) String getPort(): 获取此 URL 的端口号
5 ) StringgetProtocol(): 获取此 URL 的协议名称
6 ) String getQuery() : 获取此 URL 的查询字符串部分
7 ) URLConnectionopenConnection(): 返回一个 URLConnection 对象,它表示 到 URL 所引用的远程对象的连接。
8 ) InputSreamopenStream(): 打开 与 URL 的连接 , 返回用于读取 URL 资源的 InputStream 。
9 ) StringgetAuthority() :获得此 URL 的授权部分 。
10 ) Object getContent() :获得此 URL 的内容 。
11 ) intgetDefaultPort() :获得 与此 URL 关联协议的默认端口号 。
12 ) String getRef() :获得此 URL 的锚点 (也称为 " 引用 " )。
13 ) StringgetUserInfo() :获得此 URL 的 userInfo 部分 。
14 ) booleansameFile(URL other) : 比较两个 URL ,不包括片段部分。
15 ) protected voidset(String protocol, String host, int port, String file, String ref) :设置 URL 的字段。
16 ) static voidsetURLStreamHandlerFactory(URLStreamHandlerFactory fac) :设置应用程序的 URLStreamHandlerFactory 。
7 、 package com.hbsi.tcp;
import java.net.*;
import java.io.*;
public class UrlDemo1 {
/**
* @param args
*/
publicstatic void main(String[] args) throws Exception {
//TODO Auto-generated method stub
URLaURL=newURL("http://java.sun.con:80/docs/books/tutorial"+"index.html?name=networking#DOENLOADING");
System.out.println("protocol="+aURL.getProtocol());
System.out.println("authority="+aURL.getAuthority());
System.out.println("host="+aURL.getHost());
System.out.println("port="+aURL.getPort());
System.out.println("path="+aURL.getPath());
System.out.println("query="+aURL.getQuery());
System.out.println("filename="+aURL.getFile());
System.out.println("ref="+aURL.getRef());
}
}
结果:
protocol=http
authority=java.sun.con:80
host=java.sun.con
port=80
path=/docs/books/tutorialindex.html
query=name=networking
filename=/docs/books/tutorialindex.html?name=networking
ref=DOENLOADING
8 、实例:通过 URL 来下载网页信息
package com.hbsr.net;
import java.net.*;
import java.io.*;
public class TextNet {
/**
* @param args
*/
publicstatic void main(String[] args) throws Exception {
//TODO Auto-generated method stub
URLurl=new URL("http://www.baidu.com");
InputStreamin=url.openStream();
BufferedReaderbrin=new BufferedReader(new InputStreamReader(in));
FileOutputStreamfos=new FileOutputStream("F:\\putdown.html");
Stringline=null;
while((line=brin.readLine())!=null){
fos.write(line.getBytes());
System.out.println(line);
}
brin.close();
fos.close();
}
}
9 、 URLDecoder 类和 URLEncoder 类。
1 ) URLDecoder 类和 URLEncoder 类用于完成普通字符串和 application/x-www-form-urlencoded MIME 字符串之间的相互转换。
2 )当 URL 地址中包含有非西欧字符的字符串是,系统就会将这些非西欧字符串转换成特殊字符串,编程过程中可能会涉及将普通字符串和这种特殊字符串进行转换,此时就需要使用 URLDecoder 类和 URLEncoder 类。
3 ) URLDecoder 类 包含一个 decode ( String s , String enc )静态方法,它可以将看上去是 乱码的特殊字符串转换成普通字符串。
4 ) URLEncoder 类 包含一个 encode ( String s , String enc )静态方法,他可以将普通字符串转换成 application/x-www-form-urlencodedMIME 字符串。
5 )实例说明:
packagecom.hbsi.tcp;
importjava.io.*;
importjava.net.*;
public classUrlDemo2 {
/**
*@param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 将 application/x-www-rorm-urlencoded 字符串解码
try {
String str1 = URLDecoder.decode("Java%E5%BC%80%E5%8F%91","UTF-8");
System.out.println(str1);
// 将普通字符串转换成 application/x-www-rorm-urlencoded 字符串
Stringstr2=URLEncoder.encode("java 程序开发 ","UTF-8");
System.out.println(str2);
} catch(UnsupportedEncodingException e) {
// TODO Auto-generatedcatch block
System.out.println(e.getMessage());
}
}
}