文章目录
- 第一章 Java网络编程入门
- 1.前言
- 1.1 进程之间的通信
- 1.2 计算机网络概念
- 1.3 网络模型之OSI参考模型
- 1.4 TCP/IP参考模型和TCP/IP协议
- 1.4.1 模型解析
- 1.4.2 IP协议
- 1.4.3 TCP协议和端口
- 1.4.4 RFC简介
- 1.4.5 客户/服务器通信模式
- 1.5 用Java编写客户/服务器程序
第一章 Java网络编程入门
1.前言
我们都知道打开一个浏览器,输入一个URL
如:http://baidu.com,这个地址指向的网址就会从远程的Web服务器传到客户端,然后在浏览器里边显示出来。
再者,李四给张三送信,李四不用把信亲自送到张三家,而是将信用信封包装起来,然后通过邮政网络完成送信,张三收到信,拆开信封,读取信息。也就是说我们不用知道怎么发,我们知道发什么就行了。
所以,服务器程序和客户程序也一样,只关心发送什么样的数据给对方,而不需要考虑如何将这些数据传输给对方。传输数据任务由计算机网络完成。
这样我们就能得到如下图的网络传输过程:
本章学习内容:1、计算机基本概念;2、计算机分层模型及具体网络协议;3、使用一个例子体验如何创建简单的Java服务器和Java客户程序。
其中第二点是重点!
1.1 进程之间的通信
我们知道进程就是一个程序运行,线程就是进程中的任务。
下面来看一个例子:
package 网络编程;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 第一章1.1 进程之间的通信演示。
* @author Mr Lin,time 2020-6-4
* @version 1.0
*/
public class Chapter1_1_EchoPlayer {
public String echo(String msg){
return "echo"+msg;
}
public void talk()throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//转换流
String msg = null;
while((msg=br.readLine())!=null){
System.out.println(echo(msg));
if("bye".equals(msg))
break;
}
}
public static void main(String[]args) throws IOException {
new Chapter1_1_EchoPlayer().talk();
}
}
运行这个程序,则启动jvm虚拟机,进入main线程,该进程对应的main线程从本地控制台获取输入流和输出流。本地控制台就是一个用户交互的界面,用户通过该界面与进程交换
问题的引出:
1、上面程序的echo(String msg)方法负责生成响应结果
,但如果我们要生成响应的程序移动到了远程主机
,那么上面的程序就无法满足需求了。这种情况怎么做?
解决:
这个时候,我们需要两个程序——1是客户程序EchoClient ;2是服务器程序EchoServer.
-
客户程序EchoClient
:负责与用户交互,从本地控制台获得标准输入流和输出流;同时可以和远程的EchoServer通信,向EchoServer发送用户输入的字符串 -
EchoServer程序
:负责接受EchoClient发过来的字符串,然后把响应的结果发送回去给EchoClient
也就是说客户机运行EchoClient进程,远程服务器运行EchoServer进程。客户机和远程服务器是通过网络连接的两台主机
能否通信的前提是两个进程的主机都连接到了计算机网络。就好像张三和李四通话,他们能通话的前提是他们的电话都能连接到电话的网络(有信号)。此时他们只需要知道谈话的内容,而不必关心自己的话语怎么传输给对方的电话机
即传输信息任务由计算机网络来完成。我们只需要编写好客户程序EchoClient和服务器程序EchoServer。
1.2 计算机网络概念
1、
计算机网络
:指分布在不同地理区域的计算机用通信线路
互连起来的网络系统。通俗点说就是:
计算机网络就是通过电缆、电话线或者无线通信设备互连的计算机合集。2、
节点
:网络中的每台机器都是一个节点(node)
,大多数是计算机,也可以是连接了网络的打印机、路由器网关、网桥等。我们一般用主机代表计算机节点
3、
网络协议
:理解为主机之间通信的语言
,就好比人与人之间用某种语言交流。4、
主机地址
:网络中的每一台主机都有一个地址,用于标识主机的一个字节序列
,字节序列越长,可以表示的地址就越多,也就是说可以有更多的设备连入网络。5、
局域网(LAN,Local Area Network)和广域网(WAN,Wide Area Network)
:局域网
就是小范围的计算机网络,通常限制在几千米内的范围
,如一个房间、一个校园;广域网
则范围很大
,常常是一个国家或者洲,如互联网范围是全球各州。广域网就是让分布较远的局域网互联!
6、
协议与标准协议
:就好像互联网连接了众多的局域网,每个小网络内部可能使用不同的协议,那如何使得不同的网络可以交流?好比讲粤语的广东人遇到了讲温州话的温州佬,就会用普通话交流一样,会有一个标准语言——普通话
,与此类似网络
就有标准的语言——TCP/IP协议。
1.3 网络模型之OSI参考模型
1、背景
计算机网络产生之初,每个厂商都有自己的一套网络体系,它们互不相容。继而国际标准组织ISO建立了一个分委会,来研究怎么统一。这个组织提出了一个体系结构——开放系统互连(Open System Interconnection,简称OSI)
。
2、OSI模型的特点
- 开放意味着一个网络系统只要遵循OSI模型,则可以和世界上任何地方的也同样遵循OSI模型的网络系统互连。它为各种异构系统互连提供了概念性的框架。
3、OSI模型参考模型将网络分成了7层
从下到上分别是——物理层
、数据链路层
、网络层
、传输层
、会话层
、表示层
和应用层
对等层
:不同主机之间相同层称为对等层。例如,主机A中的表示层和主机B中的表示层互为对等层
4、OSI参考模型中各层主要的功能
1、
物理层(Physical Layer)
:传输信息肯定离不开物理介质,如双绞线和同轴电缆等,但是物理层不包括物理介质
。有人把物理介质当作了OSI模型的第零层。
物理层的任务
:为其上一层提供物理连接,以及规定通信节点之间的机械和电气等特性,如规定电缆和接线的类型等。传输数据
:比特
(bit);典型设备
:Hub(集线器
)2、
数据链路层(Data Link Layer)
:数据链路层负责在两个相邻的节点间的线路无差错传输以帧为单位的数据。每一帧包括一定的数据和一些必要的控制信息。
数据链路层任务
:负责建立、维持和释放数据链路的连接。
在传输数据时,如果接收方检测到所传数据出错,就要通知发送方重发这一帧。传输数据
:帧
;典型设备
:Switch(交换机
)3、
网络层(Network Layer)
:数据发送要经过很多的数据链路,也可能通过很多通信子网。此时需要网络层来做选择.其将数据链路层提供的帧组成数据包
,包里边封装了网络层包头
,包头里边含有逻辑信息——源主机和目标主机的网络地址。
网络层任务
:选择合适的网间路由和交换节点,确保数据及时传说到目标主机。
传输数据
:包
;典型设备
:Router(路由器
)
如图:主机A发送信息给B,先经过了节点1和节点4,最后达到主机B。相邻两个节点的线路就是数据链路。
数据链路层负责数据链路上的数据传输。从主机到主机B的整个路径
就是路由
,网络层就是负责选择合适的路由。4、
传输层(Transport Layer)
传输层任务
:根据通信子网的特性最佳利用网络资源,为两个端系统(源主机和目标主机)的会话层提供建立、维护和取消传输连接的功能,以可靠方式或不可靠方式传输数据。
可靠方式
:保证把源主机发送的数据正确送到目标主机;不可靠方式
:不保证把源主机发送的数据正确送到目标主机,数据可能出错或者丢失。传输单位
:报文5、
会话层(Session Layer)
该层又叫会晤层或者对话层。会话层及以上层的传输单位不再另外命名,统称为报文。
会话层任务
:管理进程之间的会话过程,即负责建立、管理、终止进程之间的会话。还通过数据中插入校验点来实现数据同步。
6、
表示层(Presentation Layer)
表示层任务
:将上层数据进行转换,保证一个主机的应用层数据可以被另外一个主机应用层理解。
表示层的数据转换包括数据加密、解密、压缩、解压和格式转换等。
7、
应用层(Application Layer)
应用层确定进程之间通信的实际用途,以满足用户的实际需求。
如浏览Web站点、收发e-mail、下载文件等。这些都可以是进程之间通信的实际用途
。
如图:当源主机向目标主机发送数据时,在源主机方
,数据由上层向下传递,每层都会给上一层传来的数据加上一个信息头(header),然后向下发出
。最后通过物理介质传输到目标主机。在目标主机方,数据从下层向上层传输,每层都会对数据进行处理,把信息头去掉。再向上层传,最后达到最上层,还原为实际数据。
封装过程
:每个层所加入的信息头有着不同的内容
,如网络层加入的信息头包括源地址和目标地址信息;传输层加入的信息头包括报文类型、源端口和目标端口、序列号和应答号等。(图中AH,PH…等分别表示各层加入的信息头。注意!数据链路层还为数据加入了信息尾DT
)
我们可以联系现实:张三给李四寄信,先要将信写上很多信息:如发件人、收件人、日期、地址等。然后还要套入信封。这才寄出去。而李四收到信,先得拆开信封,才能得到信的内容。
OSI参考模型将网络分为多个层次,每个层都明确分工,这简化了网络系统的设计过程。如:设计网络层只需要考虑如何找到一条发送数据的路径(即路由)即可。
对等层之间互相通信需要遵循一定的规则,如通信的内容和方式,这种规定称为网络协议(Protocol)。值得注意的是OSI参考模型并没有具体实现方式,它没有在各层制定网络协议,但是它为其他计算机厂商制定网络协议提供了参考框架。网络的各个层次都有相应的协议!
下面是OSI各个层的典型协议,都来自第三方
1.4 TCP/IP参考模型和TCP/IP协议
1.4.1 模型解析
由于OSI模型过于庞大和复杂,使其难以投入实际运用。但是其提出的网络分层极具指导意义!TCP/IP参考模型就是吸取了网络分层这一点,且简化了网络的层次,并在网络的各层(除去主机-网络层外)都提供了完善的协议。这些协议就构成了TCP/IP协议集,简称TCP/IP协议。是目前最流行的上商业化协议。主要用于广域网。
两种参考模型对比:
所以:TCP/IP参考模型将OSI参考模型中的会话层和表示层的功能合并到了应用层,同时将数据链路层和物理层合并到主机-网络层。
TCP/IP参考模型分为4个层次:应用层、传输层、网络互联层和主机-网络层
。每一层都有相应的协议。所以确切地说TCP/IP协议应该称为TCP/IP协议集
,它是TCP/IP参考模型中除了最底下的一层外的其他三层协议的集合!
TCP/IP协议这个协议集合中的最核心协议是IP协议和TCP协议
。主机-网络层是最底层,其协议由第三方提供。
从表中我们可以看出:应用层的FTP、TELNET、HTTP协议建立在TCP协议基础上;SNMP、DNS协议又建立在UDP协议上。
各层的主要功能介绍
1、
主机-网络层
实际上TCP/IP参考模型并没有真正提供这一层的实现,也没有提供协议。
它要求第三方实现的主机-网络层能够为上层(网络互联层)提供一访问接口,使得网络互联层能利用主机-网络层传递IP数据包
。
相关的第三方协议:2、
网络互联层
是整个参考模型的核心
。功能是将IP数据包发送到目标主机
。为了尽快发送数据,IP协议会将原始数据分为多个数据包,然后沿着不同的路径同时传递数据包
。如图:主机A发送的原始数据分为3个数据包,然后沿不同路径到达主机B,殊途同归。注意!数据包到达的先后顺序可能与发送的先后顺序不同,所以需要上层(传输层)对数据包进行重新排序,才能还原为原始数据
!
网络互联层具备连接异构网的功能
。如图所示,连接以太网和令牌环网,
虽然它们的类型不同,具有不同的拓扑结构,但是它们都向网络互联层提供了统一的访问接口,从而隐藏了下层网络的差异
!使得不同类型网络之间可以顺利传输数据包。
网络互联层采用IP协议,它规定了数据包格式,且规定了数据包寻找路由的流程。
3、
传输层
传输层功能就是使源主机和目标主机的进程可以进行会话。
传输层定义了两种不同服务质量的协议——TCP
(Transmission Control Protocol,传输控制协议)和UDP
(User Datagram Protocol,用户数据报协议)。TCP协议是可靠的面向连接的协议,能把源主机数据无差错地发送给目标主机。
在发送端,TCP协议负责把上层传下来的数据分成报文段传递给下层。在接收端,将收到的报文进行重组后递交给上层。还要处理端到端的流量控制,以避免接收方因数据接受慢而导致没有足够的缓冲区来接受发送方发送的大量数据。应用层协议很多都是建立在TCP协议基础上的。如:FTP,HTTP,TELNET;UDP协议则是一个不可靠的、无连接协议
,主要使用于不需要对报文进行排序和流量控制的场合
。它无法保证数据报接受的顺序和发送的顺序一致,甚至无法保证发送的信息都抵达目标主机
。应用层的一些协议建立于UDP协议之上,如SNMP,DNS等。4、应用层
TCP/IP协议将OSI参考模型的会话层和表示层合并到了应用层实现,针对各种各样的网络应用,应用层引入了许多协议。其中基于TCP/IP协议的主要有如下几种:
注意!!!HTTP和HTTPS的区别:HTTP是数据不加密的,HTTPS是数据经过加密的
1.4.2 IP协议
1、IP网络
IP网络就是在网络层采用了IP协议。IP网络中的每一台主机都有唯一的IP地址。
用于标识网络的主机。IP地址是一个32位的二进制序列
。为了方便在上层应用中表示IP地址,将32位二进制数每8位一组划分为了4个单元,每个单元用一个十进制整数表示,即2的8次方范围(0~255);如某台主机IP地址:192.168.1.1
——这就是IPv4标准;
新的Ipv6标准将使用128位的地址,进而大大提高了可用IP地址的数目,将成为新一代互联网协议。
2、IP地址与IP网址IP地址由两部分组成:IP网址和IP主机地址
。IP网址就是网络的地址
,IP主机地址就是网络中主机的地址
。
网络掩码就是用来确定IP地址的哪部分是IP网址,哪一部分是IP主机地址。
网络掩码与IP地址形式一致,但是有一定的限制。网络掩码的二进制序列中,前面的部分都是1,后面的部分都是0,如IP地址假设:192.166.3.4,其网络掩码为255.255.255.0,将网络掩码和IP地址进行与操作可得IP网址
。因此IP地址192.166.3.4的IP网址为192.166.3.0
即主机IP地址&网络掩码=IP网址
从图中我们可以看出,一个网络可以对应多个主机IP地址,IP地址(主机的地址)与 网络掩码与操作得到的IP网址一致。
3、子网划分
一个公司可能拥有一个网址和多个主机,例如192.166.0.0允许容纳的主机地址有28*28-2=(65534);
所以主机数目过多则难以管理,进而
需要将一个网络划分为多个子网
,如将该网址划分为3个子网:
192.166.1.0
,
192.166.2.0
,
192.166.3.0
,这些子网,
很明显它们的网络掩码都是255.255.255.0
;
4、发送数据包的过程
5、域名
域名就是因为IP地址比较难记忆,
人们就发明一种字符型地址——域名(Domain Name)
。IP地址与域名一一对应。先来看一个 例子:www.javathinker.org,对应的IP地址是221.130.187.148;域名从右往左表述其意义。最右边是顶层域,最左边则是机器名。一般的表示:主机机器名.单位名.网络名.顶层域名。
如:mail.xyz.edu.cn中mail表示xyz学校的一个主机机器名,xyz表示一个学校,edu为中国教育科研网,cn是中国。顶层域一般是网络机构所在的国家或者地区名称缩写。如何将域名和IP地址进行转换?
图解
6、URL(统一资源定位器)
URL(Uniform Resource Locator)统一资源定位器,专为标识网络资源位置而设置的一种编址方式。大家熟悉的网址地址就是URL,一般由3部分组成:
1.4.3 TCP协议和端口
1、TCP协议
IP协议在发送数据时,途中遇到各种事情可能导致丢包,如路由器崩溃,又如一个包沿低速链路移动而另一个包沿高速链路移动而超过前面的包导致的顺序错乱
而TCP协议使两台主机的进程能够顺利通信,而不必担心丢包或者包的顺序搞乱。
因为TCP会跟踪包顺序,并且在包的顺序搞乱时对包进行重新排序。如果包丢失则TCP会请求源主机重新发送包。
2、端口
一个很简单的问题,主机A的进程QQ向另一台主机B的QQ发送信息
IP协议会根据主机B的IP地址,把A主机QQ进程发送的信息送达到主机B
- 接下来TCP需要决定把数据发送到主机B中的哪个进程。
TCP采用端口来区分进程
,端口不是物理设备,而是标识进程的一个逻辑地址,更加确切地说是用于标识TCP连接的端点的逻辑地址。主机B的QQ被主机B随机分配了一个未被占用的端口,假设是80,而主机A的QQ端口是1000,则意味着两个进程进行了一次通信就建立了一次端到端的TCP连接。两个端点就是用端口标识。此时主机B进程QQ的地址是B:80,主机A进程QQ的地址是A:1000。每个进程都有了唯一的地址,TCP就能保证数据顺利送达特定的进程。
端口号的范围:0——65535,其中0到1023端口号一般固定分配给一些服务。
如21端口分配给FTP服务,25端口分配给SMTP(简单邮件传输)服务。80端口分配给HTTP服务(超文本传输)等。端口1024到65535端口号供用户自定义服务使用
。例如我们自己创建的EchoServer服务使用8000端口,程序运行时就会占用该端口,程序结束才释放该端口。客户进程的端口一般由主机所在的操作系统动态分配,当客户进程需要与服务器进程TCP连接,操作系统就会给客户进程分配一个未被占用的端口来使用
,客户进程和服务器进程断开连接时端口才被释放。此外还要指出TCP和UDP都是使用端口标识
,在一个主机中,TCP端口与UDP端口的取值范围是各自独立的!
允许取值相同的TCP端口值和UDP端口值
1.4.4 RFC简介
1.4.5 客户/服务器通信模式
1.5 用Java编写客户/服务器程序
需求:将开头的程序中的客户端移动到远程主机,怎么实现通信?
1、服务端代码
package SocketProgram;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Chapter1_5_EchoServer {
private ServerSocket serverSocket;//创建监听端口的对象
public Chapter1_5_EchoServer(int port) throws IOException {
//端口
serverSocket = new ServerSocket(port);
System.out.println("EchoServer start!");
}
public String echo(String msg) {
return "echo:" + msg;
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();//获取输出流对象,用于做出响应。
return new PrintWriter(socketOut, true);//使用装饰类
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();//获取输入流对象,用于接受请求。
return new BufferedReader(new InputStreamReader(socketIn));//使用转换流和装饰类
}
public void service() {
while (true) {//不断监控端口
Socket socket = null;
try {
socket = serverSocket.accept();//等待客户端连接。一旦返回socket对象意味着与一个客户连接。
System.out.println("New connection accepted" + socket.getInetAddress() +
":" + socket.getPort());
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg;
while ((msg = br.readLine()) != null) {//接受请求信息
System.out.println(msg);//打印
pw.println(echo(msg));//做出响应
if (msg.equals("bye")) {
break;//发来bye中止while,退出连接
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();//断开连接
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[]args) throws IOException {
new Chapter1_5_EchoServer(9000).service();
}
}
2、客户端程序代码:
package SocketProgram;
import java.io.*;
import java.net.Socket;
public class Chapter1_5_EchoClient {
private String host = "localhost";//主机地址为本机。
private Socket socket;
public Chapter1_5_EchoClient(int port) throws IOException {
socket = new Socket(host,port);
}
public static void main(String[]args) throws IOException {
new Chapter1_5_EchoClient(9000).talk();
}
private void talk() {
try{
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg=localReader.readLine())!=null){
pw.println(msg);//发送信息
System.out.println(br.readLine());//接受服务器的响应。
if(msg.equals("bye")){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
socket.close();
}catch (IOException e){e.printStackTrace();}
}
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();//获取输出流对象,用于做出响应。
return new PrintWriter(socketOut, true);//使用装饰类
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();//获取输入流对象,用于接受请求。
return new BufferedReader(new InputStreamReader(socketIn));//使用转换流和装饰类
}
}
客户端发送请求并收到服务端的响应:
服务器端接受到请求: