------- android培训、java培训、期待与您交流! ----------
java 是号称网络上的语言!
1、网络编程(概述):
a) 网络模型:
i. OSI(开放式系统互联参考模型)参考模型;
1. 这个模型是学习、研究时使用的模型。
2. 图片:
3. 网络传输分为了 7层。
a) 每一层都有自己所对应的协议。
i. 传输层最常见的协议TCP和UDP;
1. TCP:传输控制协议;
2. UDP:用户数据报协议。
ii. 网际层最常见的协议IP协议(IP地址的协议)。
iii. HTTP、FTP是应用层协议。
4. 学习网络编程主要就是在网际层和传输层混。
a) 在网际层和传输层进行直接的数据传输,并不走应用层,应用层要涉及应用层协议了。
5. Java Web开发在应用层混。
ii. TCP/IP参考模型;
1. 这个模型是实际网络中运行的模型。
2. 应用最为广泛的模型。
b) 网络通讯要素:
i. IP地址InetAddress;(IP地址的协议IP协议位于网络层)
1. 网络中设备的标识;
a) 只有处于网络中的设备才有IP地址。
b) 处于互联网上的任何一条机器的IP地址都不相同。
c) 一个服务器可以对应多个IP地址;
d) 一个IP地址也可以对应多个服务器。比如百度服务器要为很多人提供服务,肯定不止一台服务器在工作。
2. 不易记忆,可用主机名表示。
a) IPv4的地址由4段,每段一个字节,共4个字节32位组成。
b) 由于4段IP不够用了(也即IPv4地址即将耗尽),所以又有了6段IP地址了(也即IPv6地址)。
i. IPv6地址不光可以包含数字而且也可以包含字母。
3. 本地回环地址:127.0.0.1(本机默认的IP地址)主机名:localhost (本机默认的主机名)。是默认的说明都能改成其他的。
a) 只要你的机器上安装了网卡,就有这个IP地址了。可以用于测试网卡是否能够使用。
b) 还有一些IP地址被保留了,一般不用于公网上,而用于局域网内部。
ii. 端口号(端口号在java在java中没必要封装成对象,因为就一个整数嘛):
1. 用于标识(能够进行网络通信的)进程的逻辑地址,不同进程的标识。
a) 即便是这个网络进程没有进行网络通信,这个网络进程也有一个端口号。
b) 一个网络应用程序可以有一个或多个端口号。
i. 因为一个网络应用程序往往有多个(需要进行网络通信)的网络进程。
2. 有效端口:0~65535,其中0~1024系统程序使用或叫做保留端口。
a) 一般的应用程序不使用保留端口。
b) 常用端口:
i. Web服务:80 ;
ii. Tomcat: 8080(默认);
iii. MySQL: 3306(默认);
iii. 传输协议;
1. 也即网络上通讯的规则。
2. 常见协议:TCP/UDP 。
a) 这个协议既能用于广域网又能用于局域网。
b) 大多数操作系统里面都安装的有这个协议,是一个国际通用协议。
c) 为了安全起见。很多特有的组织或单位,他们的通信方式和我们不一样。这些组织或单位他们有自己特定的协议来进行内部通讯。所以外界想要入侵,必须也要安装这个特定的协议。否则连跟这些组织或单位通讯的资格都没有,就更不要说入侵了。
TCP和UDP 两个常用的网络传输协议,这两个协议都位于传输层,IP地址的协议IP协议位于网络层:
2、UDP:
a) 将数据及源和目的封装成数据包中,不需要建立连接;
i. 因为不需要建立连接,所以是面向无连接的。
ii. 面向无连接的意思是说,进行通信的两端,不一定非要建立连接(比如对方的相应端口没有开放啦,这个IP地址的机器没有上网啦等等)。只要知道了对方的地址就向对方发送数据。
iii. 如果真的没有建立成功连接,那么发送的数据包将会被丢弃。
b) 每个数据报的大小在限制在64k内;这个特性有点怀疑是不是真的!?
c) 因无连接,是不可靠协议;
i. 在发送过程中容易丢弃数据包。
d) 不需要建立连接,速度快;
e) UDP支持一对一、一对多、多对一、多对多的交互通信。
f) 应用:
i. 使用QQ聊天、FeiQ聊天就是使用的UDP 。
ii. 网络视频也是使用的UDP 。
iii. 桌面共享也是使用的UDP 。
3、TCP:
a) 建立连接(也即面向连接的),形成传输数据的通道;
b) 在连接中进行大数据量传输;
c) 通过三次握手完成连接,是可靠协议;
i. 也就是通讯双方之间总共进行三次通讯,来建立连接。
d) (每一个数据包发送前)必须建立连接,效率会稍低。
e) 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。
i. TCP连接是一条虚连接而不是一条真正的物理连接。
f) 应用:
i. 下载使用TCP。
4、Socket :网络编程也就是 Socket编程。
a) 每一条TCP连接有两个端点。这两个端点不是主机,不是主机的IP地址,不是应用进程,也不是运输层的协议端口。TCP连接的端点叫做套接字或插口也即Socket 。
i. 端口号拼接到IP地址即构成了套接字。
1. 套接字 socket = (IP地址:端口号)
ii. 因此每一条TCP连接唯一地被通信两端的两个端点(也即两个套接字)所确定。
1. TCP连接 ={socket1 ,socket2}={( IP1:port1),( IP2:port2)}
b) Socket就是为网络服务提供的一种机制。
c) 通信的两端必须得都有Socket。
d) 网络通信其实就是Socket间的通信。
e) 数据在两个Socket间通过IO传输。
f) 总结:
i. TCP连接的端点是套接字。
ii. 同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以出现在多个不同的TCP连接中。
iii. UDP中同样也有Socket套接字。Socket并不是仅仅只能用来表示TCP。
iv. 建立的一条TCP连接(或者UDP连接)的两端的Socket(套接字)中的端口号可以是不同的。这是经过验证的!
5、IP地址在java中的体现:
a) public class InetAddress extends Object implements Serializable
i. IP地址对象,此类表示互联网协议 (IP) 地址。
ii. IP 地址是 IP 使用的 32 位或 128 位无符号数字。
iii. 它是一种低级协议,UDP 和 TCP 协议都是在它的基础上构建的。
iv. InetAddress 的实例包含 IP 地址,还可能包含相应的主机名(取决于它是否用主机名构造或者是否已执行反向主机名解析)。
v. 这个类的构造函数都是private型的。
vi. 这个类的对象打印的是本机名(也即计算机名。比如我的是admin-PC)和本机(在网络中的)IP地址。
b) 对于网络上的一台计算机。我肯定可以拿到其IP地址。但是不能保证一定可以拿到其主机名。
i. 因为这个主机名我这里(使用DNS)有可能解析不了。
1. 解析不了是因为其IP地址和对应的主机名的映射没有在网络上注册。
ii. 拿不到其主机名,那么主机名返回的还是IP地址。
c) InetAddress类的常用方法:
i. static InetAddress getLocalHost();
ii. String getHostAddress(); //重点掌握,因为主机名还得需要解析
iii. String getHostName();
iv. static InetAddress getByName(String host);
1. 在给定主机名(也可以是IP地址)的情况下确定主机的 IP 地址。
2. host - 指定的主机名(也可以是IP地址),或 null。
v. public static InetAddress[] getAllByName(String host) throws UnknownHostException
1. 返回一个InetAddress数组是因为一个服务器可以对应多个IP地址;
2. 一个IP地址也可以对应多个服务器。
6、UDP在java中的体现(UDP传输):
a) UDP中同样也有Socket套接字。
b) DatagramSocket与DatagramPacket ;
i. DatagramSocket(数据报包Socket):
1. 此类表示用来发送和接收数据报包(DatagramPacket)的套接字。
2. 其一个无参的构造函数:
a) public DatagramSocket() throws SocketException
i. 构造数据报套接字并将其绑定到本地主机上任何可用的端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。也即本机IP地址是固定的,而端口是随机选定的。
ii. DatagramPacket(数据报包):
1. 用于封装数据包。
2. 数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
3. 数据报包DatagramPacket类中封装有:
a) 实体数据;
i. 实体数据用DatagramPacket(数据报包)对象里面那个字节数组缓冲区buf用来存储。
ii. 并且这个节数组缓冲区buf只存储实体数据。
b) 目的主机的IP地址和端口;
c) 源主机的IP地址和端口。
4. 数据报包分为:
a) 数据发送报包:用来封装数据。
i. 在使用DatagramPacket类的构造函数构造数据报包时,凡是指明了目的IP地址和端口的。就是数据发送报包。
b) 数据接收报包:用来接收数据。
i. 在使用DatagramPacket类的构造函数构造数据报包时,没有指明IP地址和端口的,并且(在以后的过程中)始终没有为该数据包设置IP地址和端口的。就是数据接收报包。
5. 所有DatagramPacket(数据报包)对象里面那个字节数组缓冲区buf只是用来存储实体数据的。
a) 这个字节数组缓冲区buf可以通过DatagramPacket对象的getData()方法来获取。
c) UDP发送接收数据的流程:
i. 建立发送端,接收端。
ii. 建立数据包。
iii. 调用Socket的发送接收方法。
iv. 关闭Socket。
d) 发送端与接收端是两个独立的运行程序。
i. 因为是两个独立运行的程序:
1. 所以要么每一端程序都使用多线程;
2. 要么每一端程序里面都带一个main函数。
目的是让这两端都能够独立运行。
e) 发送端具体流程:
i. 在发送端,要在数据包对象中明确目的地IP及发送端口。
DatagramSocket ds = new DatagramSocket();
byte[] by = “hello,udp”.getBytes();
DatagramPacket dp = new DatagramPacket(by, 0, by.length,
InetAddress.getByName(“127.0.0.1”),10000);
ds.send(dp);
ds.close();
这些方法很多都抛出了编译时异常,所以要记得捕获之或抛出之。
f) 接收端具体流程示例代码:
i. 在接收端,要想接收到数据(要对建立的接收端)DatagramSocket 必须指定监听的端口。
ii. 定义一个接收数据报包,此时这个数据报包里面还没有数据呢。只有在调用过receive方法过后,这个接收数据报包里才可能有数据。
1. 因为要使用这个接收数据报包接收(在这个监听端口上)被DatagramSocket对象监听到的字节数据。
2. 因为这个接收数据报包对象中有更多功能可以提取字节数据中的不同数据信息。
DatagramSocket ds = new DatagramSocket(10000);//这时ds已经在10000
//端口上监听到了发送端发送过来的数据发送报包(这个包是以
//字节数据形式体现的)了。但是还需要一个数据接收报包来接
//收这些监听到的字节数据。
while(true) //因为接收端必须随时保持运行才可能监听到发送端发送过来的数据
//报包,所以必须要使用这个while循环,来使接收端一直保持运行。假如
//不使用while循环,后果可想而知。并且千万不能把上面定义ds的那句代
//码也写到这个while循环里面来。否则将会出现BindException。原因见。
{
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by,by.length);//此时dp中还没
//有数据呢。
//通过socket服务的receive方法将监听到的字节数据接收进已定义好的数据 //报包dp中。receive方法是一个阻塞式方法,接收不到数据,就一直处于阻塞状态。
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength());
System.out.println(str+"--"+dp.getAddress());//获取的是发送端的地址。其实在接收端通过getXxx()方法获取到的数据字节、地址、端口号都是发送端的。
}
ds.close();
g) 总结:
i. 在UDP通信的两端中其实质也是两个Socket(套接字)在进行通讯。
ii. 并且这两端的两个套接字里面的端口号可以是不相同的。
1. 但是发送端所发送的数据报包中包含的目的端IP地址和目的端口号必须要和目的端的Socket(套接字)中的IP地址和端口号相同。
2. 同理源IP地址和源端口号…………
iii. 出现BindException的原因是:
1. 试图将套接字绑定到本地地址和端口时发生错误的情况下,抛出此异常。这些错误通常发生在端口正在使用中或无法分配所请求的本地地址时。
a) 大部分抛出这个异常的情况都是因为你需要绑定的端口正被别的应用程序使用着。比如上面接收端while循环处的注释说的。
h) 练习:
i. UDP-通过把(键盘录入方式取得的)数据发送到目的端
1. 通常只要用到java.net包就一定会用到java.io包。
2. 如果害怕你定义的buf装不下发送过来的数据报包。你可以定义一个64K大小的字节数组缓冲区buf=new byte[1024*64]。因为UDP的数据报包的大小不能超过64K。
3. 因为接收端要一直接收数据,所以要一直保持运行开放的状态。所以最后也可以不用去调用接收端的close()方法了。如果你调用了关闭方法了也无所谓。
4. 当网络号不变,主机号全为1的IP地址是一个广播地址。广播地址用于对这个网络上的所有主机广播发送数据报包。
5. 当网络号不变,主机号全为0的IP地址表示的是这个网络。
ii. UDP聊天程序(很重要):
通过键盘录入获取要发送的信息。
将发送和接收分别封装到两个线程中。
7、TCP在java中的体现(TCP传输):
a) Socket(客户端套接字)和ServerSocket(服务器端套接字)
b) 建立客户端和服务器端
c) 建立连接后,通过Socket中的IO流进行数据的传输
d) 关闭socket
e) 同样,客户端与服务器端是两个独立的应用程序。
f) 基本思路(客户端):
i. 客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
ii. 连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。
iii. 与服务端通讯结束后,关闭Socket。
g) 客户端:
i. 通过Socket建立对象并指定要连接的服务端主机以及端口。
Socket s = new Socket(“192.168.1.1”,9999); //建立客户端Socket对象。
//通过这个客户端Socket对象指定到服务器端的连接。因为tcp是面
//向连接的。所以在建立socket服务时,必须要有服务端存在,并连
//接成功。形成通路后,再在该通道进行数据的传输。
OutputStream out = s.getOutputStream(); //因为要把数据发送出去,所以要
//获取输出流对象。
out.write(“hello”.getBytes());
s.close();
h) 基本思路(服务端):
i. 服务端需要明确它要处理的数据是从哪个端口进入的。也即服务端要明确绑定的端口。
ii. 当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该客户端对象与客户端通过IO流进行数据传输。
iii. 当该客户端访问结束,关闭该客户端。
iv. 一般情况下。服务器端要一直保持运行,用以监听客户端发送来的TCP数据报包。
i) 服务端:
i. 建立服务端需要绑定一个端口。
ServerSocket ss = new ServerSocket(9999); //建立服务端对象。用于绑定这
//个9999端口。
while(true) //使用while循环的目的和UDP的类似,是为了让服务器端能够一
//直运行着,从而保证能够及时接收到连接过来的客户端。
{
Socket s = ss.accept (); //监听并接收(获取)连接过来的客户端的Socket 对象。因为可以有多个客户端连接到服务器端上,所以为了保证服务器端能向某个特定的客户端发送数据(而不会发送到别的客户端上)。所以要获取这个特定的客户端Socket对象。这个accept 方法是阻塞式的,当没有客户端连接到这个服务器端时就会一直等下去。
InputStream in = s.getInputStream(); //因为要把数据接收进来,所以要
//获取输入流对象。客户端如果发过来数据,那么服务端要使用
//对应的客户端对象,获取到该客户端对象的读取流来读取发过来的数据。
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close(); //关闭连接进来的客户端。服务器端有可能不关闭自己,但必须要在适
//当的时候关闭连接进来的客户端。因为要释放掉连接进来的客户端所占用的服
//务器系统资源。
}
ss.close(); //关闭服务器端是一个可选操作,因为服务器一般不会关闭。
j) 构造一个空的Socket对象。然后可用其connect方法连接一个套接字。
i. public void connect(SocketAddress endpoint) throws IOException
k) SocketAddress类是一个抽象类。其子类只有:
i. InetSocketAddress ;此类实现 IP 套接字地址(IP 地址 + 端口号)。也即此类封装了IP地址和端口号。
1. InetAddress封装了IP地址;
2. InetSocketAddress封装了IP套接字地址。
l) public ServerSocket(int port ,int backlog) throws IOException //ServerSocket类的一个构造函数。
i. 参数backlog —队列的最大长度。也即参数backlog指定能连接到该服务器的这个端口上的客户端的最大同时个数。因为一个服务器能同时连接的客户端的数量是有限的,超过backlog的值后,就不让再连接这个服务器了。
8、总结:
a) UDP分的是发送端和接收端;
i. 对于UDP而言,发送端和接收端,谁先启动都可以。因为UDP是面向无连接的。如果先启动了发送端,那么发送端发送的数据会被丢弃而已。
b) TCP分的是客户端和服务器端。
i. 对于TCP而言,因为TCP是面向连接的,所以必须要先启动服务器端,必须要保证发送出去的数据能够被服务器端接收到。如果先启动了客户端,那么客户端将会抛出ConnectException异常(这是一个编译时异常,这个异常是SocketException异常的子类)。这是经过验证的!!
1. 并且即便没有事先try-catch这个异常。但是在编译的时候,没有编译失败,而是在运行程序的时候才抛出这个编译时异常的。
ii. 在TCP连接已经建立(也即客户端和服务器端已经建立了连接)的情况下:
1. 如果单方面突然断开服务器端(也即单方面关闭服务器)那么客户端将会抛出SocketException异常(这个异常是IOException异常的子类)。这是经过验证的!!
2. 如果服务器端单方面突然断开和某一个客户端的连接(也即虽然服务器没有关闭,但是服务器单方面突然关闭了(由accept方法获取到的)Socket套接字对象。)那么客户端同样将会抛出SocketException异常(这个异常是ConnectException异常的子类)。这也是经过验证的!!
3. 如果单方面突然断开客户端(也即单方面关闭客户端)那么服务器端不会抛出异常。
a) 那么将会导致服务器端和客户端的TCP连接断开,并且服务器端通过accept方法得到的Socket套接字对象不可用。
b) 如果存在(此Socket套接字对象产生的)输入流对象。那么也将会导致这个输入流对象不可用。从而导致这个输入流对象的读取方法返回读取结束标记(-1 或者null)。
c) 因为TCP通讯前必须要建立可靠的TCP连接。(也即TCP是面向连接的)所以TCP不像UDP那么上来都迫不及待地发送数据。而是先通过创建客户端Socket来建立到服务器端的连接。
d) 对于TCP的客户端和服务器端,这两端的Socket套接字对象是两个不同的Socket套接字对象:(这是经过验证的)
i. 客户端创建的Socket套接字对象。这个Socket套接字对象里面封装的是:
1. (所连接的服务器端的)IP地址(也即对端的IP地址);
2. (所连接的服务器端的)监听端口号(也即对端的端口号);
3. 本端的发送端口号;
ii. 服务器端通过accept方法获得的Socket套接字对象。这个Socket套接字对象里面封装的是:
1. (所连接的客户端的)IP地址(也即对端的IP地址);
2. (所连接的客户端的)发送端口号(也即对端的端口号);
3. 本端的监听端口号;
e) 思考:对于Web服务器而言,当有多个客户端同时访问服务器时,服务端又如何提 供服务呢?
i. 如果服务器端还是使用像第 i) 条中示例代码中使用的while循环那样来进行处理。那么将会出现:
1. 有一个人进来的,服务器端进行处理。还没处理完呢,第二个人又进来了。那么这时因为还没处理完第一个人的请求,所以服务器端暂时不会处理任何人的请求当然也包括第二个人的请求(也即还没有循环到再次执行(使用)accept方法来接待第二个人进行处理。)那么第二个人就因连接不进来而等待着(也即和服务器端的TCP连接还没有建立起来)。
ii. 为了可以让多个客户端同时并发访问服务端。那么服务器端最好是将每个客户端封装到一个单独的线程中,这样,服务器端就可以同时处理多个客户端请求了。所以服务器端应该使用多线程的技术。
1. 只要明确了每一个客户端要在服务器端执行的代码即可。将该代码存入run方法中。
2. 思想:其实使用多线程就是让服务器端能够一直监听并接收连接进来的客户端。连接进来一个客户端,就在服务器端创建一个客户端的线程来处理这个客户端请求。
a) 一直监听并接收连接进来的客户端和创建客户端线程的代码就封装到主线程里面就行了。
b) 处理客户端请求的代码封装到另一个线程run方法里面就OK了。
iii. 哈哈哈,只要明白了这个,所有的服务器就都可以明白了。因为所有的服务器都是这个原理。
f) 重要示例之——TCP-客户端并发登录。
g) 对于TCP的两个Socket套接字端的
i. 输出流:一般使用的有OutputStream(使用频率比较多)、PrintWriter(最多)、BufferedWriter。
1. 一般使用BufferedWriter的地方都可以用PrintWriter来代替。
ii. 输入流:一般使用的有InputStream(比较多)、BufferedReader(最多)。
9、Tcp传输最容易出现的问题:
a) 客户端连接上服务端,两端都在等待,没有任何数据传输。
b) 通过例程分析:
i. 因为read方法或者readLine方法是阻塞式。
ii. 我认为本质原因还是Socket套接字里面没有(像文件那样的)文件开始标记和文件结束标记。
1. Socket套接字只是(注意:只是)用来存储客户端或服务器端写进来的数据的,Socket自身并没有流的结束标记。
c) 解决办法:
i. 自定义结束标记。
ii. 使用Socket对象的shutdownInput,shutdownOutput方法。
1. shutdownOutput方法:
a) 向这个Socket套接字里面写入一个流的结束标记。
i. 当其他基于这个Socket套接字的输入流读取这个Socket套接字里面存储的数据时,就可以读到这个结束标记。从而结束读取方法(而不至于阻塞到这个读取方法上)。
ii. 如果没有通过这个shutdownOutput方法向这个Socket套接字里面写入一个流的结束标记。那么当其他基于这个Socket套接字的输入流读取这个Socket套接字里面存储的数据时,(直到读取完Socket里面的数据)也不会读取到Socket里面(存储的数据)的结束标记。从而使这个读取方法认为这个流是可用的,而一直阻塞下去。
b) 如果这个Socket套接字对象调用了这个方法shutdownOutput,那么这个Socket套接字对象就不能再使用其输出流了。否则该输出流将会抛出IOException异常。
i. 自己想想也就是这样的,比如你向Socket里面写入一些数据。数据写完了就添加一个结束标记,那么就自然而然的希望,不要再向这个Socket里面写入数据了。因此 shutdownOutput方法还有“禁用此套接字的输出流”这项功能就不足为奇了。
iii. shutdownInput方法:
1. 至今还没有碰到使用这个方法的例子。
iv. 总结:
1. 对于网络编程这块,只有是出现了没有显示运行结果的问题。我觉得,就去重点关注这些阻塞式方法,一般都能解决问题了。一般都是这些因为各种各样的原因导致了这些阻塞式方法的阻塞。
10、练习:
a) 客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
i. 示例见当前文件夹文件
b) 需求:建立一个文本转换服务器。客户端给服务端发送文本,服务端会将文本转成大写在返回给客户端(客户端发送一行文本,服务器端就转换一行文本并返回给客户端)。而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。
i. 示例见当前文件夹文件 TransTextDemo.java 。非常非常重要的示例,一定要看到的啊!里面有很多有价值的内容哦!
c) (通过客户端)向服务器端上传一个文件。
i. 也很重要哦,一定也要看哦。
d) 建立一个群聊服务端。很简单,使用UDP的广播地址就OK了。
e) TCP-客户端向服务端上传一个图片。
f) TCP-客户端并发上传图片:重要
g) 客户端向服务端发送用户名请求登陆,服务端通过验证,返回“欢迎光临”,未通过“用户不存在”。
h) 第24天-04-网络编程(浏览器客户端-自定义服务端):做一个服务器端,让浏览器这个客户端来访问。
i. 浏览器就是一个标准的客户端。
ii. telnet :是一个win里面的dos命令,是一个远程登录命令。也可以当做一个客户端远程登录工具软件使用。可以连接网络中的任意一台主机,然后可以对这台主机进行命令式的配置,比如配置网络中的一台路由器或者交换机。
i) 第24天-05-网络编程(浏览器客户端-Tomcat服务端):
i. 就是演示如何使用Tomcat服务器向(客户端)浏览器显示网页的,没什么实质性内容,所以这一节没有笔记。
j) 第24天-06-网络编程(自定义浏览器-Tomcat服务端):模拟浏览器向Tomcat服务器发送访问请求。
i. 其实原理就是:获取到浏览器发送给Tomcat服务器的http信息而已,然后通过在这个自定义的客户端获取到Tomcat反馈回来的信息数据。
k) 第24天-07-网络编程(自定义图形界面浏览器-Tomcat服务端):
i. 做出来的效果:可以在文本区域里面显示Tomcat服务器发送过来的数据。但是就是不能像浏览器那样显示不了html代码的效果。
1. 浏览器能够解析html代码是因为浏览器里面挂载了许多的解析引擎。比如,Html解析引擎、CSS解析引擎、javascript解析引擎等等。
ii. 并且我们自定义界面的浏览器获取的数据还比一般的浏览器获取的数据多了http响应消息头(http响应消息头和Tomcat服务器反馈来的消息体之间也用空行隔开)。
1. 因为一般的浏览器是应用层软件,而我们自定义界面的浏览器是直接基于传输层的(TCP协议的)。
a) 应用层的浏览器在获取到传输层的数据报包后,把有关的http协议的信息都给拆掉了(也即向上层传输数据报包要拆包)。
b) 而我们自定义界面的浏览器接到传输层的数据后,因为不需要再向上层(也即应用层)传输了,所以就没有拆掉有关的http协议的信息。
l) 第24天-08-网络编程(URL-URLConnection):
i. 既然有Socket类和ServerSocket类了为啥还要有URL类和URLConnection类呢?
1. 我感觉因为Socket类和ServerSocket类是基于传输层(封装了TCP协议),用于直接获取传输层的数据。而要想获得到应用层的数据,还得把获取到的传输层的数据向上层传递并进行拆包变成应用层的数据。
2. 而URL类和URLConnection类是基于应用层的。可以通过URL对象或者URLConnection对象很轻松、很方便地获取到应用层的数据,而不用像Socket和ServerSocket那样获取到数据后还得进行拆包等麻烦的操作了。
ii. URL对象:(简单)
1. 由于URL路径比较常用,并且自己操作这个对象也挺复杂麻烦。所以java就把这个URL封装成了对象。
2. 这个对象的构造函数要抛出编译时异常:MalformedURLException。
a) 这异常表示URL是非法的、错误的,并且无法解析该URL。
3. URL类里面的常用方法:
a) String getFile() 获取此 URL 的文件名。
b) String getHost() 获取此 URL 的主机名(如果适用)。
c) String getPath() 获取此 URL 的路径部分。
d) int getPort() 获取此 URL 的端口号。
i. 如果在定义URL的时候,没有具体指定端口号,那么这个URL对象在调用这个方法的时候将返回 -1 。
e) String getProtocol() 获取此 URL 的协议名称。
f) String getQuery() 获取此 URL 的查询部分。
g) public final InputStream openStream() throws IOException
i. 此方法是 openConnection().getInputStream() 的缩写。
ii. 注意:只有获取输入流的方法而没有获取输出流的方法。
iii. URLConnection URL连接对象或 URL连接器:(掌握,3G里面也用这个对象)
1. 它代表应用程序(也即创建并使用URLConnection对象的应用程序,一般情况下,这个应用程序位于本地主机上。)和URL之间的通信链接。
a) 是一个基于应用层的对象。创建的这个对象里面其实就封装了Socket对象。所以其子类应该是封装了应用层协议的类。
2. URLConnection类是一个抽象类。
a) 其所有子类为:
i. HttpURLConnection ;
1. 支持 HTTP 特定功能的 URLConnection,是一个支持http协议的类。
ii. JarURLConnection ;
1. 连接到 Java ARchive (JAR) 文件或 JAR 文件中条目的 URL Connection,是一个支持jar协议的类。
3. 此类的实例可用于读取和写入此 URL 引用的资源。
4. 通常,创建一个到 URL 的连接需要几个步骤:
a) 通过在 URL 上调用 openConnection 方法创建连接对象(这个方法返回的是存在于应用层的URLConnection类的子类对象)。
i. 也即只要调用一个URL实例的openConnection方法,那么就会去连接URL所表示的那台主机。(具体地说也就是这个方法获取本地主机上的某个应用程序(创建并使用URLConnection对象的应用程序,一般情况下,这个应用程序位于本地主机上)和 URL 之间的连接对象)
ii. 每次调用此 URL 的 openConnection 方法都打开一个新的连接。(b、c、d步都没有什么用处)
b) 处理设置参数和一般请求属性。
c) 使用 connect 方法建立到远程对象的实际连接。
d) 远程对象变为可用。远程对象的头字段和内容变为可访问。
5. URLConnection类的常用方法:
a) public InputStream getInputStream() throws IOException
i. 这个方法是(URLConnection对象里面封装的)Socket对象的方法。
b) public OutputStream getOutputStream() throws IOException
i. 这个方法也是(URLConnection对象里面封装的)Socket对象的方法。
c) 注意:URLConnection对象没有关闭该对象的方法。
6. 模拟的一个更加类似于浏览器的自定义图形界面的浏览器,只是不能像真正的浏览器那样解析html标记。
m) 第24天-10-网络编程(域名解析):
i. 域名解析过程:
1. 先找本机上的C:\Windows\System32\drivers\etc中的hosts文件。比如127.0.0.1和localhost的映射关系就在本机上。
a) 在这个hosts文件中查找是否有所要访问的主机名和IP地址的映射关系。如果找到了拿过来用就可以了,就不去查找网络上的DNS服务器了;如果没找到,那么进行第2步。
2. 再在网络上的DNS服务器上找。
a) DNS服务器上记录的有主机名到相对应的IP地址的映射。
b) 关于自己电脑上Internet协议(TCP/IP)属性中是否配置DNS服务器:
i. 如果没有配置,则自动走那个为你提供网络服务的ISP的DNS服务器。
ii. 如果你自己配置了其他的DNS服务器那么就走你配置的这个DNS服务器。
ii. 如果在浏览器的地址栏里面直接输入IP地址,那么就直接找这个主机去了。而不会再有域名解析这个过程了。
n) 对于本机上的C:\Windows\System32\drivers\etc中的hosts文件。可以自己修改这个hosts文件。
i. 比如可以让127.0.0.1对于应多个主机名。
1. 127.0.0.1 localhost
2. 127.0.0.1 myhost
3. 127.0.0.1 www.baidu.com
ii. 比如还可以把经常访问的网站的网址和其对应的IP地址写到这个hosts文件中。可以有效地提高访问这个网页的速度。
iii. 比如有些需要付费的软件在启动的时候会到其网站上验证这个软件是使用验证码。那么我们也可以把这个网站的网址写到这个hosts文件中对于127.0.0.1,让其验证失败。哈哈哈。
iv. 对于一些恶意网站也可以把这些网站的网址和127.0.0.1写到这个hosts文件中。用以防止访问到这些恶意的网站。
v. 这样修改都可以。
o) 用于扫描某台主机上的可用端口的程序。
i. 原理就是:对于这台主机上的所有端口都建立Socket对象。创建成功,表明这个端口可用;如果创建失败,表明这个端口没有开放,不可用。
p) 对于网络架构的两种模型:
i. C/S :Client/Server 客户端,服务端。
1. 特点:
a) 1,在客户端和服务端都需要编写软件。
b) 2,维护较麻烦。
2. 好处:可以减轻服务端的压力,如网络游戏。
ii. B/S :Browser/Server 浏览器,服务端。
1. 特点:
a) 1,客户端不用单独编写软件。
1. 因为客户端用的就是浏览器。
b) 2,对于软件升级,只要考虑服务端即可。
2. 弊端:所有的程序都运行在服务端,客户端的浏览器毕竟解析能力较弱。对游戏等。
11、通常只要用到java.net包就一定会用到java.io包。
12、事物只要是复制的都应该封装成对象,可以简化操作。因为面向对象可以将复杂事物简单化。
13、对于流对象关联的设备。
a) 有自己的结束标记(必须是流可以读取到的结束标记)的有:
i. 文件:在IO流里面文件是最好操作的。
b) 没有自己的结束标记的有:
i. 键盘:对应的是System.in 。
ii. Socket套接字对象。
c) 对于没有结束标记的设备。在读取这些设备上的数据的时候为了防止读取方法被一直阻塞下去。可以:
i. 可以自己定义结束标记。
1. 比如对于键盘来说,当读取到over的时候表示结束。
2. 但是为了更加妥当一下,可以使用一个时间戳来代替over作为结束标记更好一些。
ii. 可以通过这些设备(或者关联这些设备的流对象的)特有方法来定义结束标记。
1. 比如对于Socket套接字对象来说,可以使用其shutdownOutput方法来向Socket套接字对象里面的数据末尾添加结束标记。
14、对于java中比较复杂的操作或数据结构,并且这个操作或数据结构相对常 用。那么就可以去java API 里面找是否有相关的对象可以使用。
a) 如果有这样现成的对象,那么直接拿过来用就OK了。
b) 如果没有这样的对象,那么就需要自己封装这样的对象了。
i. 通常,一般情况下都需要封装这样的对象。这也符合java面向对象的特点。