1. 概述
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程主要工作是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。
网络编程的主要问题:
- 如何准确定位到网络上一台或多台主机
- 定位到主机后如何进行通信
网络网络编程要素:IP,端口号,UDP通信协议,TCP通信协议。
2. IP
IP 可唯一定位一台网络上的计算机。
本机 IP 地址为 localhost:127.0.0.1
Java 中 使用 InetAddress 类表示Internet协议(IP)地址。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class TestInetAddress {
public static void main(String[] args) throws UnknownHostException {
// 查询本机地址
InetAddress inetAddr1 = InetAddress.getByName("127.0.0.1");
InetAddress inetAddr2 = InetAddress.getByName("localhost");
InetAddress inetAddr3 = InetAddress.getLocalHost();
System.out.println(inetAddr1);
System.out.println(inetAddr2);
System.out.println(inetAddr3);
// 查询网站 ip 地址
InetAddress inetAddr4 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddr4);
// 常用方法
System.out.println(inetAddr4.getCanonicalHostName()); // 规范名
System.out.println(inetAddr4.getHostAddress()); // IP
System.out.println(inetAddr4.getHostName()); // 域名
}
}
3. PORT
端口是设备与外界通讯交流的出口。
命令行查看端口命令
# 查看所有端口
netstat -ano
# 查看指定端口
tasklist|findstr "80"
# 查看指定端口的进程
tasklist|findstr "80"
Java InetSocketAddress
public class TestInetSocketAddress {
public static void main(String[] args) {
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);
InetSocketAddress socketAddress1 = new InetSocketAddress("localhost", 8080);
System.out.println(socketAddress);
System.out.println(socketAddress1);
System.out.println(socketAddress.getAddress());
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress.getPort());
}
}
4. 通信协议
TCP/IP 协议
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
应用场合 | 少量数据 | 传输大量数据 |
速度 | 慢 | 快 |
三次握手
(1) 建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认。
(2) 服务器收到syn包,必须确认客户端的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
(3) 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
四次挥手
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端。
(4) 客户端发回ACK报文确认,并将确认序号设置为收到序号加1。
5. TCP
通信
客户端
- 连接服务器
- 发送消息
/**
* TODO
* 客户端
* @author why
* @since 2021/11/11 14:19
*/
public class Client {
public static void main(String[] args) {
InetAddress serverIp = null;
OutputStream os = null;
try {
// 1. 服务器 IP,PORT
serverIp = InetAddress.getByName("127.0.0.1");
int port = 9999;
// 2. 创建 Socket 连接
Socket socket = new Socket(serverIp, port);
// 3. 发送消息
os = socket.getOutputStream();
os.write("hello!".getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端
- 建立服务端口
- 等待连接
- 接收消息
/**
* TODO
* 服务端
* @author why
* @since 2021/11/11 14:19
*/
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
// 1. 服务器地址
serverSocket = new ServerSocket(9999);
// 2. 等待客户端连接
socket = serverSocket.accept();
// 3. 读取客户端消息
is = socket.getInputStream();
// 套一层管道流,防止乱码
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len=is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
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 (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件上传
服务端
/**
* TODO
* 服务端
* @author why
* @since 2021/11/11 15:15
*/
public class Server {
public static void main(String[] args) throws IOException {
// 1. 创建服务
ServerSocket serverSocket = new ServerSocket(9999);
// 2. 监听客户端连接
Socket socket = serverSocket.accept();
// 3. 获取输入流
InputStream is = socket.getInputStream();
// 4. 文件输出
FileOutputStream fos = new FileOutputStream(new File("./resources/receive/test.txt"));
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
// 5. 通知客户端接收完毕
OutputStream os = socket.getOutputStream();
os.write("receive over !".getBytes());
// 6. 关闭资源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
客户端
/**
* TODO
* 客户端
* @author why
* @since 2021/11/11 15:09
*/
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建 Socket 连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);
// 2. 创建输出流
OutputStream os = socket.getOutputStream();
// 3. 读取文件
FileInputStream fis = new FileInputStream(new File("./resources/send/test.txt"));
// 4. 写出文件
byte[] buffer = new byte[1024];
int len;
while ((len=fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 5. 传输完毕
socket.shutdownOutput();
// 6. 确认服务器接收完毕
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] infoBuffer = new byte[1024];
int infoLen;
while ((infoLen=is.read(infoBuffer)) != -1) {
baos.write(infoBuffer, 0, infoLen);
}
System.out.println(baos.toString());
// 7. 关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
}
6. UDP
发送端
/**
* TODO
* UDP 客户端,不需要建立连接
* @author why
* @since 2021/11/11 16:04
*/
public class Client {
public static void main(String[] args) throws IOException {
// 1. 建立 Socket
DatagramSocket socket = new DatagramSocket();
// 2. 建立一个包
String msg = "Hello!";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
// 3. 发送包
socket.send(packet);
// 4. 关闭流
socket.close();
}
}
接受端
/**
* TODO
* UDP 服务端
* @author why
* @since 2021/11/11 16:10
*/
public class Server {
public static void main(String[] args) throws IOException {
// 1. 开放端口
DatagramSocket socket = new DatagramSocket(9090);
// 2. 接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(), 0, packet.getData().length));
// 2. 关闭连接
socket.close();
}
}
7. URL
统一资源定位符:定位互联网资源。
DNS 域名解析:将 ip 映射成为域名,或将域名解析为 ip。
构成 :协议://ip:端口/项目名/资源
。