1 基本概念
1.1 什么计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源
1.2 计算机网络的主要功能
1)资源共享
2) 信息传输与集中处理
3) 均衡负荷与分布处理
4) 综合信息服务(www/综合业务数据网络(ISDN))
1.3 什么是网络通信协议
计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传 输控制步骤、出错控制等制定标准。
1.4 网络通信接口
为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间 能进行信息交换。接口包括两部分
-
硬件装置: 实现结点之间的信息传送
-
软件装置:规定双方进行通信的约定协议
1.5 为什么要分层
由于结点之间联系很复杂,在制定协议时,把复杂成份分解成 一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。
1.6 通信协议的分层规定
把用户应用程序作为最高层,把物理通信线路作为最低层,将其间的协议处理分为若干层,规定每层处理的任务,也规定每层的接口标准。
³ 1、物理层(Physical Layer)
³ 2、数据链路层(Data Link Layer)
³ 3、网络层(Network Layer)网络层协议的代表包括:IP、IPX、RIP、OSPF等
³ 4、传输层(Transport Layer)传输层协议的代表包括:TCP、UDP、SPX等。
³ 5、会话层(Session Layer)NetBIOS、ZIP(AppleTalk区域信息协议)等。
³ 6、表示层(Presentation Layer) ASCII、ASN.1、JPEG、MPEG等
³7、应用层(Application Layer) Telnet、FTP、HTTP、SNMP等
2 . TCP和UDP
在网络通讯中,TCP方式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。而UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。
这两种传输方式都是实际的网络编程中进行使用,重要的数据一般使用TCP方式进行数据传输,而大量的非核心数据则都通过UDP方式进行传递,在一些程序中甚至结合使用这两种方式进行数据的传递。
由于TCP需要建立专用的虚拟连接以及确认传输是否正确,所以使用TCP方式的速度稍微慢一些,而且传输时产生的数据量要比UDP稍微大一些。
TCP(transfer control protocol)是面向连接的,所谓面向连接,就是当计算机双方通信时必需先建立连接,然后数据传送,最后拆除连接三个过程
并且TCP在建立连接时又分三步走:
第一步是请求端(客户端)发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号;
第二步,服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)。
第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成。然后才开始通信的第二步:数据处理。
这就是所说的TCP三次握手(Three-way Handshake)。
IP
端口:
3 InetAddress: 封装计算机的ip地址和DNS.
Java为了可移植性,不允许直接调用操作系统,而是由java.net包来提供网络功能。Java虚拟机负责提供与操作系统的实际连接。Java.net中包含如下重要的类:
InetAddress: 封装计算机的ip地址和DNS.
这个类没有构造函数。如果要得到对象,只能通过静态方法:getLocalHost, getByName, getAllByName, getAddress, getHostName 抛出的异常有:java.net.UnknownHostException
只有IP和主机名,没有端口的程序段:
/*只有IP和主机名,没有端口:*/
InetAddress ia1 = InetAddress.getLocalHost();
System.out.println(ia1.getHostName());
InetAddress ia2 = InetAddress.getByName("www.163.com");
System.out.println(ia2.getHostAddress());
System.out.println(ia2.getHostName());
包含端口,用于socket通信的:InetSocketAddress
/*包含端口,用于Socket通信的InetSocketAddress*/
InetSocketAddress isa1 = new InetSocketAddress("127.0.0.1",8080);
System.out.println(isa1.getHostName());
InetSocketAddress isa2 = new InetSocketAddress("www.hao123.com",80);
System.out.println(isa2.getAddress());
2 . URL 和URL类
URL:
在WWW上,每一信息资源都有统一的且唯一的地址,该地址就叫URL(Uniform Resource Locator),它是WWW的统一资源定位符。URL由4部分组成:协议 、存放资源的主机域名、资源文件名和端口号。如果未指定该端口号,则使用协议默认的端口。例如,http 协议的默认端口为 80。
在Java.net包中提供了URL类,该类封装了大量复杂的涉及从远程站点获取信息的细节。
URL类:
P地址唯一标识了Internet上的计算机,而URL则标识了这些计算机上的资源。类 URL
代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
为了方便程序员编程,JDK中提供了URL类,该类的全名是java.net.URL,有了这样一个类,就可以使用它的各种方法来对URL对象进行分割、合并等处理。
基本测试:
try {
URL u = new URL("http://www.google.com.hk:8080/webhp?hl=zh-CN&sourceid=cnhp");
System.out.println("获取与此url关联的协议的默认端口:"+u.getDefaultPort());
System.out.println("getFile:"+u.getFile()); //端口号后面的内容
System.out.println("主机名:"+u.getHost()); //www.google.cn
System.out.println("路径:"+u.getPath()); //端口号后,参数前的内容
System.out.println("端口:"+u.getPort());//如果www.google.cn:80则返回80.否则返回-1
System.out.println("协议:"+u.getProtocol());
System.out.println("参数部分:"+u.getQuery());
System.out.println("锚点:"+u.getRef());
URL url = new URL("http://www.baidu.com/");
basicSpider(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
一个小型的网络爬虫
/**
* 读取baidu 主页的内容更,再用本机的IE 浏览器 ,将其打开
* 一个小型的网络爬虫
* @param url
*/
public static void basicSpider(URL url){
InputStream is ;
BufferedReader reader;
StringBuffer sb = new StringBuffer();
String temp;
String result;
File file;//
String path ;//路径
FileWriter fw;
Runtime run = Runtime.getRuntime();//启动计算机中的进程
Process pro = null;
try {
// 得到输入流
is = url.openStream();
reader = new BufferedReader(new InputStreamReader(is));
while((temp=reader.readLine()) != null){
sb.append(temp);
}
//System.out.println(sb.toString());
result = sb.toString();
Properties pros = System.getProperties();
path = pros.getProperty("user.dir");
path = path+File.separator+"res"+File.separator+"baidu.html";
//System.out.println(path);
file = new File(path);
fw = new FileWriter(file);
if(!file.isDirectory()){
byte[] arrs = result.getBytes(); //得到对应的字节流
file.setWritable(true); //设置文件写
int len = result.length();
fw.write(result, 0, len);
}
//启动本地的IE浏览器,打开对应的文件
pro = run.exec("C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE "+path);
is.close();
reader.close();
fw.close();
//Thread.sleep(10000);
//pro.destroy();
run.addShutdownHook(new Thread(){
public void start(){
}
});
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e){
System.out.println(e.getClass().getName());
if("UnknownHostException".equals(e.getClass().getName())){
System.out.println("连接超时……,系统自动重新连接");
basicSpider(url);
}
}
}
3. SOCKET 通信和编程
在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。
“请求-响应”模式:
Socket类:发送TCP消息
ServerSocket类:创建服务器
套接字是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的ip地址。端口地址是指客户端或服务器程序使用的主机的通信端口。
在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性,将两个Socket进行连接,这样,客户端和服务器通过套接字所建立连接使用输入输出流进行通信。
TCP/IP套接字是最可靠的双向流协议,使用tcp/ip可以发送任意数量的数据。
实际上,套接字只是计算机上已编号的端口。如果发送方和接收方计算机确定好端口,他们就可以通信了。
TCP/IP通信连接的简单过程:
位于A计算机上的tcp/ip软件向B计算机发送包含端口号的消息,B计算机的TCP/IP软件接收该消息,并进行检查,查看是否有他知道的程序正在该端口上接收消息。如果有,他就将该消息交给这个程序。要使程序有效地运行,就必须有一个客户端和一个服务器。
通过SOCKET编程的顺序
1.创建SocketServer,在创建时监听对应的端口,这个端口用来接收客户端发送的消息
2. SocketServer调用acept() 方法 ,使之位于阻塞状态
3. 创建客户端Socket对象,并设置IP 和端口号
4. 建立和服务器的连接
5. 分别取得客户端和服务器端的Socket 的InputStream 和 OutputStream
6. 利用Socket来和ServerSocket通信
测试代码如下:
服务器端代码:
public class ImproveServerSocket{
public static void main(String[] args){
try {
//创建服务器端 套接字
ServerSocket server = new ServerSocket(8888);
System.out.println("服务器已经启动…………");
//接受客户端套接字
Socket socket = server.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
while(true){
String str = in.readLine();
System.out.println("客户端说:"+str);
String str2 ="";
str2 = br.readLine(); //读到 \n 为止
out.write(str2+"\n");
out.flush();
if("end".equals(str2)){
break;
}
}
in.close();
out.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码:
public class ImproveClientSocket {
public static void main(String[] args){
try {
//创建服务器端套接字
Socket server = new Socket(InetAddress.getLocalHost(),8888);
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(server.getOutputStream()));
BufferedReader in = new BufferedReader(
new InputStreamReader(server.getInputStream()));
while(true){
String msg = br.readLine();
//定要加。不加的话,那边按照readLine读的话,如果没读到\n就会一直阻塞!
out.write(msg+"\n");
out.flush();
if("end".equals(msg)){
break;
}
System.out.println("服务器端说:"+ in.readLine());
}
out.close();
in.close();
br.close();
server.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
};
}
}
3. UDP 通信和编程
Socket通信是一种基于tcp协议,建立稳定连接的点对点的通信。这种通信方式实时、快速、安全性高、但是他很占用系统的资源。
在网络传输方式上,还有另一种基于UDP协议的通信方式,称为数据报通信方式。在这种方式中,每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地。
DatagramSocket:用于发送或接收数据包
当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。
DatagramSocket有两种构造函数。一种是无需任何参数的,常用于客户端。
常用方法:send,receive, close
UDP端口和TCP端口是相互独立的。
DatagramPacket:数据容器(封包)的作用
常用方法:构造函数、getAddrress(获取发送或接收方计算机的Ip地址)、getData(获取发送或接收的数据),setData(设置发送的数据)
UDP通信编程的基本步骤:
public class Server {
public static void main(String[] args){
try {
byte[] b = "你好".getBytes();
//创建服务器端的DatagrapmSocket
//本身占用9000 端口来发送数据包
DatagramSocket ds = new DatagramSocket(9000);
//服务器端传送 DatagrammPacket
//必须告诉数据包的地址
DatagramPacket dp = new DatagramPacket(b,
b.length,InetAddress.getLocalHost(),8089);
ds.send(dp);
ds.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Client {
public static void main(String[] args){
try {
//创建客户端的DatagramSocket
DatagramSocket ds = new DatagramSocket(8089);
byte[] b = new byte[1024];
//客户端接收数据的DatagrammPacket
DatagramPacket dp = new DatagramPacket(b,b.length);
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength());
System.out.println(str);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过ByteArrayInputStream、ByteArrayOutputStream可以传递基本类型数据:
public class Client {
public static void main(String[] args) throws Exception {
long n = 2000L;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeLong(n);
byte[] b = bos.toByteArray();
//必须告诉数据包要发到哪里去
DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost",8999));
//我本身占用9000端口向外面机器发数据包
DatagramSocket ds = new DatagramSocket(9000);
ds.send(dp);
ds.close();
}
}
public class Server {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(8999);
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b,b.length);
ds.receive(dp); //阻塞式方法
ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData());
DataInputStream dis = new DataInputStream(bis);
System.out.println(dis.readLong());
ds.close(); }
}
传递对象时的情况:
class Person implements Serializable{
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
}
public class Client {
public static void main(String[] args) throws Exception {
Person person = new Person(20,"aa");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(person);
byte[] b = bos.toByteArray();
//必须告诉数据包要发到哪里去
DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost",8999));
//我本身占用9000端口向外面机器发数据包
DatagramSocket ds = new DatagramSocket(9000);
ds.send(dp);
ds.close();
}
}
public class Server {
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(8999);
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b,b.length);
ds.receive(dp); //阻塞式方法
ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData());
ObjectInputStream ois = new ObjectInputStream(bis);
Person person = (Person) ois.readObject();
System.out.println(person.name);
ds.close();
}
}