JAVA从入门到放弃24—网络编程基础/InetAddress/InetSocketAddress/URL/UDP/TCP/Socket
01 网络编程基础
- 计算机网络分类
局域网:(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。
城域网:(Metropolitan Area Network,MAN)是在一个城市范围内所建立的计算机通信网,简称MAN。属宽带局域网。MAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,
广域网:(Wide Area Network,WAN),又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程网。广域网并不等同于互联网。
互联网:(Internet)属于传媒领域。又称国际网络,互联网始于1969年美国的阿帕网。是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。通常internet泛指互联网,而Internet则特指因特网。
- 计算机网络的主要功能
资源共享:凡是入网用户均能享受网络中各个计算机系统的全部或部分软件、硬件和数据资源,为最本质的功能。
信息传输与集中处理:实现数据信息的快速传递。
均衡负荷与分布处理:通过算法将大型的综合性问题交给不同的计算机同时进行处理。用户可以根据需要合理选择网络资源,就近快速地进行处理。
三高问题:高并发,高性能,高可用。
- 网络通信协议
计算机网络中实现通信必须有一些约定,对速率,传输代码,代码结构,传输控制步骤,出错控制等制定标准。
常见的网络通信协议有:TCP/IP协议、IPX/SPX协议、NetBEUI协议等。
网络通信接口:
为了使两个节点之间能进行对话,必须在他们之间建立通信工具(即接口),使彼此之间,能进行信息交换。
接口包括两部分:
硬件装置:实现结点之间的信息传送
软件装置:规定双方进行通信的约定协议
- 网络分层模型
OSI参考模型 |
---|
应用层 |
表示层 |
会话层 |
传输层 |
网络层 |
数据链路层 |
物理层 |
TCP/IP参考模型 |
---|
应用层 (FTP文件传送协议、SMTP简单邮件传送协议、TELNET远程登录协议、DNS域名服务、NNTP网络新闻传送协议、HTTP超文本传送协议、SNMP简单网络管理协议) |
传输层 (采用两种不同的协议TCP和UDP,TCP提供可靠连接,UDP为应用层提供无连接的尽力服务) |
互联网层 (IP,解决了网络互联问题,但它是一个不可靠的传输协议,在传输过程中可能会出现IP报文的错误,丢失和乱序等问题。与IP一起工作的还有ICMP(Internet Control Message Protocol),ARP(Address Resolution Protocol),RARP(Reverse ARP)等协议 ) |
网络接入层 (向互联网层提供标准接口) |
物理层 |
数据进入协议栈的封装过程:用户数据(应用层)---------->压入TCP首部(传输层)---------->IP数据报(压入IP包头)----->以太网帧(压入Mac地址)(网络接入层)---------->比特流(物理层)
02 IP地址与Java中的InetAddress类
为了实现全网互联需要两个基本条件,一是全网统一编址,而是路由算法。编址解决如何区分网络中的节点(路由器、服务器)、用户终端等。在Internet中是采用IP地址来区分路由器、服务器、用户计算机等。
每一个主机域名都有一个对应的真实IP地址,用来标识网络中的节点。
关于IP的相关操作
win+r运行cmd打开命令管理器
输入ipconfig可以查看本机IP地址
输入ping “ip地址” 可以连接该ip地址对应的电脑(在该电脑无防火墙的情况下)
IP地址分为五类,A类保留给政府机构,B类分配给中等规模的公司,C类分配给任何需要的人,D类用于组播,E类用于实验,各类可容纳的地址数目不同。
A类地址:10.0.0.0~10.255.255.255 (1个)
B类地址:172.16.0.0~172.31.255.255 (16个)
C类地址:192.168.0.0~192.168.255.255 (256个)
特殊IP记录
0.0.0.0 : 一个集合:所有不清楚的主机和目的网络。
255.255.255.255:限制广播地址
127.0.0.1:本机地址,主要用于测试。别名:Localhost
InetAddress 此类表示互联网协议 (IP) 地址。
注意:该类无构造方法,需要调用静态方法获得该类对象
static InetAddress getLocalHost()
返回本地主机。
static InetAddress[] getAllByName(String host)
在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。
static InetAddress getByAddress(byte[] addr)
在给定原始 IP 地址的情况下,返回 InetAddress 对象。
static InetAddress getByAddress(String host, byte[] addr)
根据提供的主机名和 IP 地址创建 InetAddress。
static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
byte[] getAddress()
返回此 InetAddress 对象的原始 IP 地址
String getCanonicalHostName()
获取此 IP 地址的完全限定域名。
String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。
String getHostName()
获取此 IP 地址的主机名。
//示例:
public class Test {
public static void main(String[] args) throws UnknownHostException {
//InetAdress类没有构造方法
InetAddress ip = InetAddress.getLocalHost();//返回本地主机
System.out.println(ip);//打印本地主机信息:主机名/主机ip地址
System.out.println(ip.getAddress());//以字节数组形式返回原始IP地址
System.out.println(ip.getHostName());//获取此 IP 地址的主机名。
System.out.println(ip.getCanonicalHostName());//获取此 IP 地址的完全限定域名。
}
}
03 端口Port与Java中的InetSocketAddress类
一台计算机可能提供多种网络应用程序,端口用来区分这些不同程序。
通过端口,可以在一个主机上运行多个网络应用程序。如MySQL(3306),Oracle(1521),Tomcat(8080)等等程序都有自己的端口。
端口的表示是一个16位的二进制整数,2个字节,对应十进制的0~65535。
公认端口:0-1023
注册端口:1024-49151
动态/私有端口:49152-65535
InetSocketAddress 此类实现 IP 套接字地址(IP 地址 + 端口号)。它还可以是一个对(主机名 + 端口号),在此情况下,将尝试解析主机名。它提供不可变对象,供套接字用于绑定、连接或用作返回值。
构造方法:
InetSocketAddress(InetAddress addr, int port)
根据 IP 地址和端口号创建套接字地址。
InetSocketAddress(int port)
创建套接字地址,其中 IP 地址为通配符地址,端口号为指定值。
InetSocketAddress(String hostname, int port)
根据主机名和端口号创建套接字地址。尝试将主机名解析为 InetAddress。如果尝试失败,则将地址标记为未解析。
注意事项:有效端口值介于 0 和 65535 之间。端口号 zero 允许系统在 bind 操作中挑选暂时的端口。 一般选用范围为注册端口的范围1024-49151
常用方法:
static InetSocketAddress createUnresolved(String host, int port)
根据主机名和端口号创建未解析的套接字地址。
InetAddress getAddress()
获取 InetAddress。
String getHostName()
获取 hostname。
int getPort()
获取端口号。
//示例:
public class Test {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",8082);
System.out.println(inetSocketAddress.getHostName());
System.out.println(inetSocketAddress.getPort());
System.out.println(inetSocketAddress.getAddress());
System.out.println(inetSocketAddress.getHostString());
}
}
04 URL统一资源定位符与Java中的URL类
网络三大基石:HTML,HTTP,URL
HTML:超文本标记语言(Hyper Text Markup Language),标准通用标记语言下的一个应用。
HTTP:超文本传输协议(Hyper Text Transfer Protocol),位于TCP/IP协议族中的应用层。用于Web浏览器和Web服务器之间的通信,但它也可以用于其他目的。 HTTP遵循经典的C-S模型,客户端打开一个连接以发出请求,然后等待它收到服务器端响应。HTTP是无状态协议,意味着服务器不会在两个请求之间保留任何数据(状态)。该协议虽然通常基于TCP / IP层,但可以在任何可靠的传输层上使用。
URL:URL是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URI = URL + URN
URI:Uniform Resource Identifier,统一资源标志符。
URL:Uniform Resource Locator,统一资源定位符。
URN:Uniform Resource Name,统一资源命名。
在www上,每一个信息资源都有统一且唯一的地址,即统一资源定位符。
如:https://localhost:8080/index.html , 由4部分组成:协议,主机域名或IP地址,端口号,资源文件名
拓展知识:
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
URL类 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
URL 可选择指定一个“端口”,它是用于建立到远程主机 TCP 连接的端口号。如果未指定该端口号,则使用协议默认的端口。例如,http 协议的默认端口为 80。
构造方法:
URL(String spec)
根据 String 表示形式创建 URL 对象。
URL(String protocol, String host, int port, String file)
根据指定 protocol、host、port 号和 file 创建 URL 对象。
URL(String protocol, String host, int port, String file, URLStreamHandler handler)
根据指定的 protocol、host、port 号、file 和 handler 创建 URL 对象。
URL(String protocol, String host, String file)
根据指定的 protocol 名称、host 名称和 file 名称创建 URL。
URL(URL context, String spec)
通过在指定的上下文中对给定的 spec 进行解析创建 URL。
URL(URL context, String spec, URLStreamHandler handler)
通过在指定的上下文中用指定的处理程序对给定的 spec 进行解析来创建 URL
常用方法:
String getAuthority()
获取此 URL 的授权部分。
Object getContent()
获取此 URL 的内容
int getDefaultPort()
获取与此 URL 关联协议的默认端口号。
String getFile()
获取此 URL 的文件名。
String getHost()
获取此 URL 的主机名(如果适用)。
String getPath()
获取此 URL 的路径部分。
int getPort()
获取此 URL 的端口号。
String getProtocol()
获取此 URL 的协议名称。
String getQuery()
获取此 URL 的查询部分。
String getRef()
获取此 URL 的锚点(也称为“引用”)。
String getUserInfo()
获取此 URL 的 userInfo 部分。
//示例:
public class Test04 {
public static void main(String[] args) throws IOException {
URL url = new URL("https://bbs.westos.org/forum-7.htm");
System.out.println("URL 为:" + url.toString());
System.out.println("协议为:" + url.getProtocol());
System.out.println("验证信息:" + url.getAuthority());
System.out.println("文件名及请求参数:" + url.getFile());
System.out.println("主机名:" + url.getHost());
System.out.println("路径:" + url.getPath());
System.out.println("端口:" + url.getPort());
System.out.println("默认端口:" + url.getDefaultPort());
System.out.println("请求参数:" + url.getQuery());
System.out.println("定位位置:" + url.getRef());
}
}
/*
运行结果:
URL 为:https://bbs.westos.org/forum-7.htm
协议为:https
验证信息:bbs.westos.org
文件名及请求参数:/forum-7.htm
主机名:bbs.westos.org
路径:/forum-7.htm
端口:-1
默认端口:443
请求参数:null
定位位置:null
*/
05 传输协议TCP和UDP
TCP/IP协议族中的运输层(也称为主机到主机层)采用两种不同的协议:TCP和UDP。
TCP(Transfer Control Protocol)为应用程序之间的数据传输提供可靠连接,它是面向连接的传输控制协议。
UDP(User Datagram Protocol)为应用层提供无连接的尽力(best-effort)服务best-effort,它并不保证一定传到,也不保证按顺序传输以及不重复传送。
TCP协议特点:面向连接
点到点的通信
高可靠性
占用系统资源多,效率低
“三次握手四次挥手”
为什么TCP协议连接的时候是三次握手,关闭的时候却是四次挥手?
答:因为TCP有个半关闭状态,假设A.B要释放连接,那么A发送一个释放连接报文给B,B收到后发送确认,这个时候A不发数据,但是B如果发数据A还是要接受,这叫半关闭。然后B还要发给A连接释放报文,然后A发确认,所以是4次。
UDP协议特点:非面向连接,
传输不可靠,可能丢失
发送不管对方是否准备好,接收方收到也不确认
可以广播发送
非常简单的协议,开销小
06 套接字
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层和传输层之间,需要使用套接字对数据进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
Socket此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
构造方法:
Socket()
通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(InetAddress host, int port, boolean stream)
已过时。 Use DatagramSocket instead for UDP transport.
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
创建一个套接字并将其连接到指定远程地址上的指定远程端口。
Socket(Proxy proxy)
创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。
protected Socket(SocketImpl impl)
使用用户指定的 SocketImpl 创建一个未连接 Socket。
Socket(String host, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(String host, int port, boolean stream)
已过时。 使用 DatagramSocket 取代 UDP 传输。
Socket(String host, int port, InetAddress localAddr, int localPort)
创建一个套接字并将其连接到指定远程主机上的指定远程端口。
常用方法:
void close()
关闭此套接字。
void connect(SocketAddress endpoint)
将此套接字连接到服务器。
void connect(SocketAddress endpoint, int timeout)
将此套接字连接到服务器,并指定一个超时值。
InputStream getInputStream()
返回此套接字的输入流。
OutputStream getOutputStream()
返回此套接字的输出流
int getPort()
返回此套接字连接到的远程端口。
ServerSocket 此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
构造方法:
ServerSocket()
创建非绑定服务器套接字。
ServerSocket(int port)
创建绑定到特定端口的服务器套接字。
ServerSocket(int port, int backlog)
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress bindAddr)
使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器
常用方法:
Socket accept()
侦听并接受到此套接字的连接
void close()
关闭此套接字。
InputStream getInputStream()
返回此套接字的输入流。
OutputStream getOutputStream()
返回此套接字的输出流
DatagramSocket 此类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达
构造方法:
DatagramSocket()
构造数据报套接字并将其绑定到本地主机上任何可用的端口。
protected DatagramSocket(DatagramSocketImpl impl)
创建带有指定 DatagramSocketImpl 的未绑定数据报套接字。
DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr)
创建数据报套接字,将其绑定到指定的本地地址。
DatagramSocket(SocketAddress bindaddr)
创建数据报套接字,将其绑定到指定的本地套接字地址。
常用方法:
void close()
关闭此数据报套接字
void connect(InetAddress address, int port)
将套接字连接到此套接字的远程地址。
void connect(SocketAddress addr)
将此套接字连接到远程套接字地址(IP 地址 + 端口号)。
void disconnect()
断开套接字的连接。
void receive(DatagramPacket p)
从此套接字接收数据报包。
void send(DatagramPacket p)
从此套接字发送数据报包。
07 基于TCP协议的Socket编程
特点:通信双方需要建立连接,连接建立时双方存在主次之分
应用:电话、视频
分析:
使用基于TCP协议的Socket网络编程实现
TCP协议基于请求响应模式
在网络通讯中,第一次主动发起通讯的程序被作为客户端程序
第一次通讯中等待连接的程序被称作服务器程序
利用IO流实现数据的传输
public class TestClient {
public static void main(String[] args) throws IOException {
Socket socket = null;
while (true){
//创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
socket =new Socket("127.0.0.1",7288);
System.out.println("server:connected successfully!");
//OutputStream getOutputStream() 返回此套接字的输出流。
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
Scanner scanner = new Scanner(System.in);
System.out.println("send:");
String line = scanner.nextLine();
dataOutputStream.writeUTF(line);
if (line.equals("bye")){
break;
}
}
socket.close();
}
}
public class TestServer {
public static void main(String[] args) throws IOException {
//开放一个端口
ServerSocket socket = new ServerSocket(7288);//创建绑定到特定端口的服务器套接字。
//ServerSocket此类实现服务器套接字。服务器套接字等待请求通过网络传入。
//
while (true){
//等待连接
Socket accept = socket.accept();//侦听并接受到此套接字的连接。
System.out.println("Connected successfully!");
//获得客户端传入的数据流
DataInputStream dataInputStream = new DataInputStream(accept.getInputStream());//包装此套接字返回的输入流。
String mes = dataInputStream.readUTF();
//读取数据
System.out.println("client:"+mes);
if (mes.equals("bye")){
System.out.println("The client has broken the connection");
break;
}
}
socket.close();
}
}
运行结果展示:
08 基于UDP协议的Socket编程
特点:通信双方不需要建立连接,通信双方完全平等
应用:QQ、微信
分析:
使用基于UDP协议的Socket网络编程实现
不需要利用IO流实现数据的传输
每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地,一切以包为中心。
DatagramSocket:用于发送或接收数据包的套接字
DatagramPacket:数据报包。
DatagramPacket:此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
构造方法:
DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int offset, int length)
构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int length, SocketAddress address)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
public class Test01 {
public static void main(String[] args) throws IOException {
//接收方
//1.开放一个端口 DatagramSocket 可注册端口1024-49151
DatagramSocket socket = new DatagramSocket(5020);//创建数据报套接字并将其绑定到本地主机上的指定端口。
//2.准备一个容器 封包 DatagramPacket
byte[] receiveBuf = new byte[1024];
DatagramPacket packet = new DatagramPacket(receiveBuf, receiveBuf.length);
//3.等待接受包
while (true){
socket.receive(packet);//从此套接字接收数据报包。
//4.读取包
String recStr = new String(packet.getData(),0,packet.getLength());
System.out.println("A:"+recStr);
if (recStr.equals("exit")){
break;
}
}
//5.释放资源,关闭端口
socket.close();
}
}
public class Test02 {
public static void main(String[] args) throws IOException {
//发送方
//1.开放一个端口 DatagramSocket 和指定目的地址和接收方端口
DatagramSocket Socket = new DatagramSocket();//构造数据报套接字并将其绑定到本地主机上任何可用的端口。
InetAddress addr = InetAddress.getByName("127.0.0.1");
int port = 5020;
//2.准备数据
byte[] sendBuf = null;
while (true){
Scanner scanner = new Scanner(System.in);
System.out.println("Send: ");
String line = scanner.nextLine();
sendBuf=line.getBytes();
//3.封装数据
DatagramPacket packet =new DatagramPacket(sendBuf, sendBuf.length,addr, port);//构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
//4.发送数据
Socket.send(packet);//从此套接字发送数据报包。
if (line.equals("exit")){
break;
}
}
//释放资源
Socket.close();
}
}
运行结果: