第九章:网络编程
- 网络编程概述
-
计算机网络:把分布在不同地理区域的计算机与专门的外部设备,用通信线路互连成一个规模大、功能强的网络系统。
-
网络编程的目的:
- 用来实现网络互连的不同计算机上运行的程序间可以进行数据交换
- 直接或间接地通过网络协议与其它计算机进行通讯
-
网络编程中有的两个主要问题:
- 如何准确地定位网络上一台或多台主机。-----IP
- 找到主机后如何可靠高效地进行数据传输。-----网络传输协议
-
Java是Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
-
Java提供的网络类库,可以实现网络连接,联网的底层细节被隐藏在Java的本机安装系统里,由JVM进行控制。并且Java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
OSI参考模型 | TCP/IP参考模型 | TCP/IP参考模型各层对应协议 |
---|---|---|
应用层 | 应用层 | HTTP 、 ftp 、 telnet 、 DNS |
表示层 | ||
会话层 | ||
传输层 | 传输层 | TCP 、 UDP |
网络层 | 网络层 | IP 、 ICMP 、 ARP |
数据链路层 | ||
物理层 | 物理+数据链路层 | Link |
- 物理层和数据链路层涉及物理介质访问和二进制数据流传输
- 网络层的主要协议有
IP
(Internet protocol)、ICMP
(Internet Control Message Protocol,互联网控制报文协议)、IGMP
(Internet Group Management Protocol,互联网组管理协议)、ARP
(Address Resolution Protocol,地址解析协议)和RARP
(Reverse Address Resolution Protocol,反向地址解析协议)等。涉及寻址和路由选择 - 传输层的基本功能是为两台主机间的应用程序提供端到端的通信。传输层从应用层接受数据,并且在必要的时候把它分成较小的单元,传递给网络层,并确保到达对方的各段信息正确无误。
- 应用层提供应用程序的网络接口
如何实现网络中的主机互相通信 :
TCP/IP协议上tcp的端口对应的各种应用程序,客户机要访问某个应用程序就会要求打开主机的这个固定的端口。而客户机自己会打开一个大于1024的随机端口用来跟对方的主机进行通信。用户使用应用程序编辑信息,一个单一的会话,实际上就是一个主机应用层之间的逻辑的软件连接。
- IP和端口号
-
唯一的标识Internet上的计算机
-
IP地址分类方式1:
IPV4
和IPV6
IPV4
:4个字节组成,4个0-255,大概42亿,30亿都在北美,亚洲4亿,2011年初已经用尽。以点分十进制表示,如192.168.0.1
IPV6
:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号( : )分开,如:3ffe:3201:1401:1280:c8ff.fe4d:db39:1984
-
IP地址分类方式2:公网地址 (万维网使用) 和 私有地址 (局域网使用)
-
私有址址 : 以
192.168
和192.168.1
开头的, 范围为192.168.0.0 - 192.168.255.255
专门为组织机构内部使用特点:不易记忆
-
-
端口号用于标识正在计算机上运行的进程 (程序)
-
不同的进程有不同的端口号
-
范围:
0 - 65535
-
端口分类:
-
公认端口:
0 - 1023
,被预先定义的服务通信占用 (如: HTTP占用端口 80,FTP占用端口 21,Telnet占用端口 23 ) -
注册端口:
1024 - 49151
分配给用户进程或者应用程序。(如: Tomcat占用端口 8080,MySQL占用端口 3306,Oracle占用端口 1521) -
动态/私有端口 :
49152 - 65535
-
端口号与IP地址的组合得出一个网络套接字
// 创建套接字 Socket socket = new Socket("192.168.219.208",9903);
-
- InetAddress类
-
InetAddress上的主机有两种表示地址:
- 域名 (hostName):
www.baidu.com
- IP地址 (hostAddress):
220.181.111.37
- 域名 (hostName):
-
InetAddress类对象含有一个 InetAddress 主机地址的域名和IP地址:
www.baidu.com 和 220.181.111.37
-
域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器 (DNS) 负责将域名转化成IP地址,这样才能和主机建立连接。 —— 域名解析
InetAddress inetAddress = socket.getInetAddress(); // 主机名与IP InetAddress.getByName("www.baidu.com"); // IP inetAddress.getHostAddress(); // 主机名 inetAddress.getHostName();
- 网络通信协议
-
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
-
通信协议分层的思想
- 由于结点之间联系很复杂,在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
-
传输层 协议中的两个协议:
- 传输控制协议 —— TCP ( Transmission Control Protocol )
- 用户数据报协议 —— UDP ( User Datagram Protocol )
-
TCP/IP 以其两个主要协议:传输控制协议 (TCP) 和网络互联协议 (IP) 而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议 。
-
IP(Internet Protocol) 协议:是 网络层 的主要协议,支持网间互连的数据通信 。
- TCP
-
使用TCP协议前,须先建立TCP连接,形成传输数据通道
-
传输前,采用“三次握手”方式,是可靠的
-
TCP协议进行通信的两个应用进程:客户端、服务端
-
在连接中可进行大数据量的传输
-
传输完毕,需释放已建立的连接,效率低
-
在断开时要进行四次挥手
-
建立TCP连接
-
服务器端
// 创建服务器端口 ServerSocket server = new ServerSocket(9903); System.out.println("等待访问"); // 建立套接字监听等待客户端连接 Socket socket = server.accept(); System.out.println("访问成功");
-
客户端
// 创建套接字 Socket socket = new Socket("localhost",9903);
-
-
三次握手
- 标志位 (Flags):共6个,即URG, ACK, PSH, RST, SYN, FIN 等
- URG:紧急指针(urgent pointer)有效
- ACK:确认序号有效
- PSH:接收方应该尽快将这个报文交给应用层
- RST:重置连接
- SYN:发起一个新连接
- FIN:释放一个连接
-
四次挥手
- UDP
-
将数据、源、目的封装成数据包,不需要建立连接
-
每个数据报的大小限制在64K内
-
因无需连接,故是不可靠的
-
发送数据结束时无需释放资源,速度快
-
服务器端 —— 有参
// 服务器有参 DatagramSocket server = new DatagramSocket(999); // 建立服务端端口 byte[] b = new byte[1024]; // 接收的数据报包的 ip与端口号信息 可有可无, // 因为对方是向你本身的端口发送的数据报包,不管你标明还是不标明数据报包的来源,你都可以接收到 DatagramPacket dp = new DatagramPacket(b,b.length); server.receive(dp); String line = new String(dp.getData(),0, dp.getLength()); System.out.println(line); server.close();
-
客户端 —— 无参
// 客户端无参 DatagramSocket client = new DatagramSocket(); InetAddress address = InetAddress.getByName("192.168.219.208"); int port = 3535; // 发送信息 String s = "你好,服务端"; byte[] b1 = s.getBytes(); // 数据报包必须有ip和端口信息,你要发出去的数据报包如果没有这两个信息, // 就相当于快递没有目的地,快递就发不出去了。 DatagramPacket dp1 = new DatagramPacket(b1,b.length,address,port); client.send(dp1); client.close();
-
- TCP通信 — Socket (套接字)
-
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。
-
通信的两端都要有Socket,是两台机器间通信的端点
-
网络通信其实就是Socket间的通信
-
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
-
注 :一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
-
Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示:
-
基本方法:
方法 | 功能 |
---|---|
InetAddress getLocalAddress() | 返回对方Socket中的IP的InetAddress对象 |
int getLocalPort() | 返回本地Socket中的端口号 |
InetAddress getInetAddress() | 返回对方Socket中IP地址 |
int getPort() | 返回对方Socket中的端口号 |
void close() throws IOException | 关闭Socket,不可在以后的网络连接中使用,除非创建新的套接字 |
InputStream getInputStream() throws IOException | 获取与Socket相关联的字节输入流,用于从Socket中读数据 |
OutputStream getOutputStream() throws IOException | 获取与Socket相关联的字节输出流,用于向Socket中写数据 |
Socket accept() throws IOException | 等待客户端的连接请求,返回与该客户端进行通信用的Socket对象 |
void close()throws IOException | 关闭监听Socket |
InetAddress getInetAddress() | 返回此服务器套接字的本地地址 |
int getLocalPort() | 返回此套接字在其上监听的端口号 |
SocketAddress getLocalSocketAddress() | 返回此套接字绑定的端点的地址 |
- 客户端
-
客户端Socket的工作过程中的四个基本步骤:
- 创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常
- 打开连接到 Socket 的输入/出流 :使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
- 按照一定的协议对 Socket 进行读/写操作 :通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程
- 关闭Socket :断开客户端到服务器的连接,释放线路
-
客户端创建Socket类对象的同时会自动向服务器方发起连接
-
Socket的构造方法:
- Socket(String host,int port) throws UnknownHostException , IOException:向服务器(域名是host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常
- Socket(InetAddress address,int port) throws IOException:根据InetAddress对象所表示的IP地址以及端口号port发起连接
-
客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求
Socket socket = new Socket("localhost",9903); OutputStream out = socket.getOutputStream(); // 发送消息 String str = s.next(); out.write(str.getBytes()); out.close();
- 服务器端
-
服务器ServerSocket( int port )的工作过程中的四个步骤:
- 调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求
- 调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象
- 调用该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出流和输入流,开始网络数据的发送和接收
- 关闭ServerSocket和Socket对象:客户端访问结束,关闭通信套接字
-
服务器建立ServerSocket对象
ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的ServerSocket对象。
所谓“接收”客户的套接字请求,就是 accept() 方法会返回一个 Socket 对象
// 创建服务器端口 ServerSocket server = new ServerSocket(9903); System.out.println("等待访问"); // 建立套接字监听等待客户端连接 Socket socket = server.accept(); System.out.println("访问成功"); InputStream in = socket.getInputStream(); byte[] by = new byte[1024]; int length = in.read(by); String message = new String(by,0,length); System.out.println("来自"+socket.getInetAddress()+":"+message); in.close();
- UDP通信
- 类
DatagramSocket
和DatagramPacket
实现了基于 UDP 协议网络程序 - UDP数据报通过数据报套接字
DatagramSocket
发送和接收。系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达 DatagramPacket
对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接
- 发送端
-
创建客户端的DatagramSocket对象:new DatagramSocket()
-
将待发送数据打包:new DatagramPacket(byte[] buf, int length, InetAddress address, int port)
-
调用DatagramSocket对象的方法发送数据:send(DatagramPacket p)
-
关闭客户端的DatagramSocket对象:close()
DatagramSocket client = new DatagramSocket(); System.out.println("请输入::"); String send = s.next(); DatagramPacket dp = new DatagramPacket(send.getBytes(),send.getBytes().length,address,yourPort); client.send(dp); client.close();
- 接收端
-
创建服务端的DatagramSocket对象:new DatagramSocket(int port)
-
创建一个数据包来存储接收到数据:new DatagramPacket(byte[] buf, int length)
-
调用DatagramSocket对象的方法接收数据:receive(DatagramPacket p)
-
调用DatagramPacket对象的方法解析收到的数据包:getData()、getLength()
-
关闭服务端的DatagramSocket对象:close()
DatagramSocket server = new DatagramSocket(myPort); byte[] b = new byte[1024]; DatagramPacket dp0 = new DatagramPacket(b,b.length); server.receive(dp0); String receive = new String(dp0.getData(),0,dp0.getLength()); System.out.println("来自"+dp0.getAddress()+":"+receive); server.close();
- URL
-
URL(Uniform Resource Locator):统一资源定位符,它表示Internet上某一资源的地址
-
它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源
-
通过URL我们可以访问Internet上的各种网络资源,比如最常见的www,ftp站点。浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源
-
URL的基本结构由5部分组成 :
传输协议 ://
、主机名:
、端口号
、文件名 ?
、参数列表
- 例:
http://92.168.1.100:8080/helloworld/indexjsp?ausername=shkstart&password=123
-
构造方法:URL(String url)
-
方法:
public string getProtocol()
获取该URL的协议名public string getHost()
获取该URL的主机名ublic string getPort()
获取该URL的端口号public string getPath(
获取该URL的文件路径public String getQuery()
获取该 URL的参数部分
- URL下载数据
-
URL url = new URL(“文件地址”)
-
HttpURLConnection http = (HttpURLConnection) url.openConnection()
-
http.connect()
-
InputStream in = http.getInputStream()
URL url = new URL("https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/css/components/user_quit_dialog-527f3ede74.css"); System.out.println(url.getProtocol());// 获得此URL的协议名称 System.out.println(url.getHost());// 获得此URL的主机名 System.out.println(url.getPort());// 获得此URL的端口号 System.out.println(url.getPath());// 获得此URL的路径部分 System.out.println(url.getQuery());// 获得此URL的查询部分 // HttpURLConnection:支持 HTTP 特定功能的 URLConnection. // 每个 HttpURLConnection 实例都可用于生成单个请求 // 请求后在 HttpURLConnection 的 InputStream 或 OutputStream 上 // 调用 close()方法可以释放与此实例关联的网络资源,但对共享的持久连接没有任何影响 // openConnection():返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接 HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection(); httpURLConnection.connect(); InputStream in = httpURLConnection.getInputStream(); FileOutputStream out = new FileOutputStream("D:/Test/baidu.html"); byte[] b = new byte[1024]; int length; while((length = in.read(b)) != -1){ out.write(b,0,length); } in.close(); out.close();
若有错误,欢迎私信指正!