网络编程概念
计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信。网络编程就是如何在程序中实现两台计算机的通信。
网络编程对所有开发语言都是一样的。用Java进行网络编程,就是在Java程序本身这个进程内,连接别的服务器进程的通信端口进行通信。
TCP/IP简介
为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
通信的时候,双方必须知道对方的标识。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。
IP
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
TCP
TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
网络通信的要素
网络编程中的两大要素:
- 如何定位网络上的一台或多台主机?
- 通过IP地址和端口号,就能定位到某一台计算机上的具体应用
- IP地址
- 端口号
- 找到主机之后如何通信?
- 网络通信协议
- TCP
- UDP
网络编程准备
IP
Java提供了针对IP地址的类:InetAddress
- IP:唯一定位网络上的一台计算机
- 常见IP:
- 127.0.0.1:本机(localhost)
- IP地址分类
- IPv4/IPv6
- IPv4:如
192.168.0.1
,4个字节,32位,每位从0-255 - IPv6:如
2001:0db8:85a3:0042:1000:8a2e:0370:7334
,8个无符号整数,128位,每位从0-255
- IPv4:如
- 公网(互联网)-私网(局域网)
InetAddress类
InetAddress类无法实例化,因为其没有构造器,只能通过其类名调用静态方法
查询本机地址的多种方法
InetAddress ip1 = InetAddress.getByName("127.0.0.1");
InetAddress ip2 = InetAddress.getByName("localhost");
InetAddress ip3 = InetAddress.getLocalHost();
查询域名的IP地址
InetAddress ip4 = InetAddress.getByName("www.lilmoon.xyz");
InetAddress常用方法
String hostName = ip4.getHostName(); // 获取ip对应的域名
String hostAddress = ip4.getHostAddress(); // 获取对应的ip地址
byte[] Address = ip4.getAddress(); // 获取地址
端口
端口表示计算机上的一个进程:
- 不同的进程端口号不同,用于区分不同进程
- 端口范围:0-65535
- 端口分为TCP端口和UDP端口,各有65536个端口,不同协议的端口号可以相同
- 端口分类:
- 公有端口:0-1023
- HTTP:80
- HTTPS:443
- FTP:21
- SFTP:22
- Telent:23
- 程序注册端口:1024-49151,分配给用户或程序
- Tomcat:8080
- MySQL:3306
- Oracle:1521
- 动态/私有端口:49152-65535
端口相关常用DOS命令:
# 查看所有端口
netstat -ano
# 查找具体的端口
netstat -ano|findstr "1024"
# Linux/Win查看指定端口的进程
tasklist|findstr "1024"
# Mac查看指定端口的进程
lsof -i :80
InetSocketAddress
InetSocketAddress类可以实例化,可以输入IP+端口号作为参数。
InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1", 80);
常用方法
System.out.println(inetSocketAddress1.getAddress()); // 获取IP地址
System.out.println(inetSocketAddress1.getHostName()); // 获取主机名
System.out.println(inetSocketAddress1.getPort()); // 获取端口
通信协议
网络通信协议涉及速率,传输码率,代码结构,传输控制等多种概念,十分复杂,所以采用了分层的思想。
TCP/IP协议簇,包含了多种协议,最要的协议有:
- TCP:用户传输协议
- UDP:用户数据报协议
- IP:网络互联协议
协议分层
OSI体系结构 | TCP/IP协议簇 | |
---|---|---|
应用层 | 应用层 | TELENT、FTP、HTTP、SMTP、DNS等 |
表示层 | ||
会话层 | ||
传输层 | 传输层 | TCP、UDP |
网络层 | 网络层 | IP、ICMP、ARP、RARP |
数据链路层 | 网络接口层 | 各种物理通信网络接口 |
物理层 |
TCP、UDP对比
TCP:
- 连接,稳定
- 三次握手,四次挥手
- 客户端/服务端
- 传输完成,释放连接
UDP:
- 不连接,不稳定
- 客服端/服务端,无明显界限
- 对方没准备好也可以发送
实现网络编程
TCP实现传递信息
客户端
客户端要做的工作:
- 提供要连接的服务端IP地址及端口号
- 创建Socket连接对象
- 发送信息(IO流)
- 最后关闭资源
代码实现:
// 1.提供服务端的地址及端口号
InetAddress serverIP= InetAddress.getByName("127.0.0.1");
int port = 9999;
// 2.创建socket连接
Socket socket = new Socket(serverIP, port);
// 3.发送消息 IO流
OutputStream os = socket.getOutputStream();
os.write("Hello Server!".getBytes());
// 4.关闭资源
os.close();
socket.close();
System.out.println("连接结束");
服务端
服务端要做的工作:
- 创建可供连接的端口
- 监听客户端连接
- 读取客户端发来的消息(通过管道流实现)
- 最后关闭资源
代码实现:
// 1.提供服务端地址
ServerSocket serverSocket = new ServerSocket(9999);
// 2.等待客户端连接
Socket accept = serverSocket.accept();
// 3.读取客户端消息
InputStream is = accept.getInputStream();
// 管道流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
// input stream的read方法用于将读到的内容写入buffer,直到全部读取完毕或buffer读满
// 没读到数据时,会返回-1
while ((len = is.read(buffer))!=-1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
// 4.关闭资源
baos.close(); // 输出完毕
is.close(); // 输入完毕
accept.close(); // 断开连接
serverSocket.close(); // 关闭服务端
TCP实现文件传输
客户端
客户端要做的工作:
- 创建socket连接
- 通过文件流读取文件
- 创建输出流
- 写出文件
- 停止输出(通知服务端已完成发送)
- 因为TCP为安全连接,等待收到服务端的收到确认
- 关闭资源
代码实现:
// 1.创建socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
// 2.文件流,读取文件
FileInputStream fis = new FileInputStream(new File("02-3.jpg"));
// 3.创建输出流
OutputStream os = socket.getOutputStream();
// 4.写出文件
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// TCP为安全连接,需要通知服务端已经发送完毕
socket.shutdownOutput(); // 停止输出
// TCP为安全连接,需要确认服务端接收到
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] check = new byte[1024];
int cnt;
while ((cnt = is.read(check)) != -1) {
baos.write(check, 0, cnt);
}
if (baos.toString().equals("Received")) {
System.out.println("Successfully sent");
}
// 5.关闭资源
baos.close();
is.close();
os.close();
fis.close();
socket.close();
服务端
服务端要做的工作:
- 提供连接的端口
- 监听客户端连接
- 通过文件流接收文件
4.创建接收流,接收文件 - 通过文件流输出文件
- 因为TCP为安全连接,需要通知客户端已接收完毕
- 关闭资源
实现代码:
// 1.提供端口
ServerSocket serverSocket = new ServerSocket(9999);
// 2.监听连接
Socket accept = serverSocket.accept();
// 3.创建接收流,接收文件
InputStream is = accept.getInputStream();
// 4.文件输出
FileOutputStream fos = new FileOutputStream(new File("Accept_02.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("Successfully received");
// TCP为安全连接,需要告知客户端接收完毕
OutputStream os = accept.getOutputStream();
os.write("Received".getBytes());
// 5.关闭资源
fos.close();
is.close();
accept.close();
serverSocket.close();