Java网络编程
编辑时间:2021/03/27
读完本节:大概花费29钟,共2909词
1.网络编程概述
- Java从语言级上提供了对网络应用程序的支持,Java提供的网络类库,可以实现便捷的网络连接,联网的底层细节被隐藏Java的本机安装系统里,由JVM进行控制,并且Java实现了一个跨平台的网络库,用户面对的是一个统一的网络编程环境
- 计算机网络:把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便的相互传递信息、共享硬件、软件、数据信息等资源。
- 网络编程的目的:直接或间接的通过网络协议与其他计算机实现数据交换,进行通讯
- 网络编程中存在的两个主要问题:
- 问题一:如何准确的定位网络上一台主机或多台主机;定位主机上特定的应用
- 问题二:找到主机之后如何可靠高效的进行数据传输
2.通信信息要素
-
IP和端口号:通过双方的IP和端口号实现在网络中准确的定位信息交互的节点
-
网络通信协议:作为一定的规则确保主机连接之后能够高效或可靠的进行数据传输,这些规则即通信协议。
-
通信协议对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
-
通过通信协议分层的思想解决了通信设计内容很多带来的麻烦;在指定协议时,把复杂成分分解成一些简单的成分,再将它们符合起来。最常用的符合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层之间互不影响,利于系统的开发和扩展
-
网络通信协议的层次模型:
-
-
通信要素:IP和端口号
-
IP地址:InetAddress
- 唯一的标识Internet上的计算机(通信实体)
- 本地回环地址(hostAddress):127.0.0.1;主机名(hostName):localhost
- IP地址分类方式1:IPv4和IPv6
- IPv4:4个字节组成,4个0-255.大概42亿,以点分十进制表示,目前已用尽
- IPv6:128位(16字节组成),写成8个无符号整数,每个整数用四个十六进制位表示,树枝间用冒号分隔,
- IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)
- 192.168.开头的就是私有地址,范围从192.168.0.0~192.168.255.255,专门为组织机构内部使用
- 实例化InetAddress的两个方法:getByName(String host)、getLocalHost();两个常用的方法getHostName()、getHostAddress()
import java.net.InetAddress; import java.net.UnknownHostException; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/27 20:31 * @Description: */ public class InetAddressTest { public static void main(String[] args) { try { InetAddress inet1 = InetAddress.getByName("192.168.137.1"); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("5gbsoh.com"); System.out.println(inet2); } catch (UnknownHostException e) { e.printStackTrace(); } } }
-
端口号:用于标识正在计算机上运行的进程
- 不同的进程由不同的端口号,两个进程的端口号相同会造成端口冲突
- 端口号被规定成16位的整数:0~65535
- 端口分类:
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
- 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)
- 动态/私有端口:49152~65535
-
端口号和IP地址的组合得出一个网络套接字:Socket
-
-
通信要素:网络协议
-
TCP/IP协议簇:
- 传输层协议中有两个非常重要的协议:
- 传输控制协议TCP:(Transmission Control Protocol)
- 用户数据报协议UDP:(User Datagram Proticol)
- TCP/IP以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议
- IP(Internet Protocol)协议是网络层的主要协议,支持王建互联的数据通信
- TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层
- 传输层协议中有两个非常重要的协议:
-
TCP和UDP
-
TCP协议:
-
使用TCP协议前,需要建立TCP连接,形成传输数据通道
-
传输前,采用“三次握手”方式,点对点通信,是可靠的
-
TCP协议进行通信的两个应用进程:客户端、服务端(B/S)
-
在连接中可进行大量数据的传输
-
传输完毕,需要释放已建立的连接(四次挥手),效率低
-
-
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据包的大小限制在64k内
- 发送不管对方是否准备好,接收方收到也不确认,故UDP是不可靠的
- 可以广播发送
- 发送数据结束时无需释放资源,开销小,速度快
-
-
3.TCP网络编程
-
例1,实现客户端发送信息给服务端,服务端将数据显示在控制台上
import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/27 21:07 * @Description: 客户端发送信息给服务端,服务端将数据显示在控制台上 */ public class TCPTest { @Test public void client(){ Socket socket = null; OutputStream os = null; try { //1. 创建Socket对象,指明服务器端的IP和端口号 InetAddress inet = InetAddress.getByName("192.168.137.1"); socket = new Socket(inet,12131); //2. 获取一个输出流,用于输出数据 os = socket.getOutputStream(); //3. 写出数据操作 os.write("c'est la vie".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server(){ ServerSocket ss = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1. 创建服务器端的ServerSocket,指明自己的端口号 ss = new ServerSocket(12131); //2. 调用accept()表示接收来自于客户端的socket socket = ss.accept(); //3. 获取输入流 is = socket.getInputStream(); //不建议,可能会有乱码 // byte[] buffer = new byte[1024]; // int len; // while ((len = is.read(buffer)) != -1){ // String str = new String(buffer,0,len); // System.out.println(str); // } //4. 读取输入流中的数据 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; int len; while ((len = is.read(buffer)) != -1){ baos.write(buffer,0,len); } System.out.println(baos.toString()); System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress()); } catch (IOException e) { e.printStackTrace(); } finally { //5. 关闭资源 if(baos != null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
从客户端发送文件给服务器端,服务端保存到本地,并输出”你还在等什么呢?“。并返回“c’est la vie”给客户端。并关闭相应的连接
import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/27 21:32 * @Description: 从客户端发送文件给服务器端,服务端保存到本地。并返回“发送成功”给客户端。并关闭相应的连接 */ public class TCPTest1 { @Test public void client(){ Socket socket = null; OutputStream os = null; FileInputStream fis = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1. socket = new Socket(InetAddress.getByName("192.168.137.1"),31674); //2. os = socket.getOutputStream(); //3. fis = new FileInputStream(new File("UnlimitedProgress.jpg")); //4. byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //重要:关闭数据的输出,否则服务端会一直等待客户端结束输出数据 socket.shutdownOutput(); //5. 接收来自服务端的数据,并显示到控制台上 is = socket.getInputStream(); baos = new ByteArrayOutputStream(); byte[] bufferr = new byte[20]; int len1; while ((len1 = is.read(bufferr)) != -1){ baos.write(bufferr,0,len1); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); } finally { //5. if(baos != null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server(){ ServerSocket ss = null; Socket socket = null; InputStream is = null; FileOutputStream fos = null; try { //1. ss = new ServerSocket(31674); //2. socket = ss.accept(); //3. is = socket.getInputStream(); //4. fos = new FileOutputStream(new File("UnlimitedProgress_bak1.jpg")); //5. byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } //6. 服务器给予客户端反馈 OutputStream os = socket.getOutputStream(); os.write("c'est la vie".getBytes()); System.out.println("你还在等什么呢?"); } catch (IOException e) { e.printStackTrace(); } finally { //7. if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(ss != null){ try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
4.UDP网络编程
-
类DatagramSocket和DatagramPackePacket实现了基于UDP协议的网络程序,
-
UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
-
DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端IP地址和端口号
-
UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接。
import org.junit.Test; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/27 22:29 * @Description: */ public class UDPTest { @Test /** 发送器 */ public void sender(){ DatagramSocket socket = null; try { socket = new DatagramSocket(); String str = "使用UDP方式发送的消息"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getByName("127.0.0.1"); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090); socket.send(packet); } catch (IOException e) { e.printStackTrace(); } finally { if(socket != null){ socket.close(); } } } @Test /** 接收器 */ public void receiver(){ DatagramSocket socket = null; try { socket = new DatagramSocket(); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(), 0, packet.getLength())); } catch (IOException e) { e.printStackTrace(); } finally { if(socket != null){ socket.close(); } } } }
5.URL编程
- URL类(Uniform Resourse Locator):统一资源定位符,表示Internet上某一资源的地址
- 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源,比如最常见的WWW,ftp,站点。浏览器通过解析给定的URL资源可以在网络上查找相应的文件或其他资源
- URL的基本结构由5部分构成:
- <传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
- #片段名:即锚点
- 参数列表格式:参数名=参数值&参数名=参数值&…&参数名=参数值
- 为了表示URL,java.net中实现了类URL。我们可以通过下面的构造器来初始化一个URL对象:
- public URL(String spec):通过一个表示URL地址的字符串可以构造一个URL对象。
- public URL(URL context, String spec):通过基URL和相对URL构造一个URL对象
- public URL(String potocol, String host, String file)
- public URL(String potocol, String host, int port, String file)
- URL类的构造器都声明抛出非运行时异常,必须要对这一异常进行处理,通常是使用try-catch语句进行捕获
- URL类常用的方法:一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法获取这些属性
- public String getProtocol():获取该URL的协议名
- public String getHost():获取该URL的主机名
- public String getPort():获取该URL的端口号
- public String getPath():获取该URL的文件路径
- public String getFile():获取该URL的文件名
- public String getQuery():获取该URL的查询名