Skip to content

网络编程

基本概念

IP地址

java
IP地址(Internet  Protocol)
唯一标识网络上的每一台计算机
IP地址的组成
32位,由4个8位二进制数组成
    
----
IP地址 = 网络地址 +主机地址
	网络地址:标识计算机或网络设备所在的网段
	主机地址:标识特定主机或网络设备

端口号

java
端口号标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号,一个 16 位的整数 0~65535

DNS

java
DNS:Domain Name System,域名系统
是一个分布式的数据库 负责将输入的域名与对应的IP地址进行匹配 解析

网络模型

java
网络模型属于使用网络进行数据传输的各个环节统称 
网络模型的存在是为了规范数据传输的各个细节 保证数据的安全性 
七层模型太过了理想化 未被实现 真正落地实现的 就是 五层模型

image-20250813105911078

image-20250813105920339

网络套接字

端口号与IP地址的组合得出一个网络套接字:Socket。

java
网络编程就是通过Socket发送和接收数据的。

Socket概念

通信链路的端点就称为“套接字”(Socket),是提供给应用程序的接口。

Socket的底层机制复杂,Java平台提供了一些简单的API,无序了解底层机制(网络)。

Socket类

用于客户端使用的;

构造方法

Socket(InetAddress host, int port)

java
作用:创建套接字并将其连接到指定的本地端口,本地地址。
    
参数:String类型的IP地址,int类型端口号
    
返回值:创建一个套接字 socket
    
实例:
// 创建客户端Socket对象 指定主机地址为本机 端口号为8899
Socket socket = new Socket("127.0.0.1",8899);

方法

getOutputStream()

java
作用:创建一个输出流,用于写数据,发送数据。
    
参数:无
    
返回值:OutputStream流对象
    
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
OutputStream outputStream = socket.getOutputStream();

shutdownOutput()

java
作用:关闭输出流。
    
参数:无
    
返回值:无
    
实例
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
socket.shutdownOutput();

getInputStream()

java
作用:创建一个输入流,用于读数据,读取数据。
    
参数:无
    
返回值:InputStream流对象
    
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
InputStream inputStream = socket.getInputStream();

getInetAddress()

java
作用:返回套接字所连接的地址。
    
参数:无
    
返回值:InetAddress类型对象
    
实例:
    
InetAddress inetAddress = socket.getInetAddress()
// 获取主机名称
String name = inetAddress.getHostName()

ServerSocket类

用于服务端使用;

构造方法

ServerSocket(int port)

java
作用:创建绑定到指定端口的服务器套接字
    
参数:int类型端口号
    
返回值:ServerSocket类型对象 server,创建绑定到指定端口的服务器套接字;
    
实例:

ServerSocket server = new ServerSocket(9090);
Socket socket = server.accept();

方法

accept()

java
作用:侦听要连接到此套接字并接受它
    
细节:等待客户端连接后,后续代码才会执行。
    
参数:无
    
返回值:Socket对象,套接字socket
    
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();

DatagramPacket类

数据报包用于实现无连接分组传送服务;UDP传输数据的包对象;

构造方法

DatagramPacket(byte[] buf, int length)

java
作用:构造一个 DatagramPacket用于接收长度的数据包
        
参数:byte类型数组,长度
    
返回值:Socket类型
    
实例:
// 准备数据
String msg = "你好,服务器";
byte[] sendData = msg.getBytes();
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length);

DatagramPacket(byte[] buf, int length, InetAddress address, int port)

java
作用:构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号
        
参数:byte类型数组,长度,地址对象,端口号
    
返回值:Socket类型
    
实例:
// 准备数据
String msg = "你好,服务器";
byte[] sendData = msg.getBytes();
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);

方法

getAddress()

java
作用:返回该数据报发送或接收数据报的计算机的IP地址
   
参数:无
    
返回值:InetAddress,IP地址信息对象
    
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455);
// 创建用于存放数据的空间
byte[] receiveData = new byte[36];
// 创建UDP包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据,并把数据存放在包对象中
socket.receive(receivePacket);
// 包对象中获取IP地址信息
// 获取客户端的IP信息
InetAddress clientAddress = receivePacket.getAddress();
// 获取端口号
int clientPort = receivePacket.getPort();

getSocketAddress()

java
作用:获取该数据包发送到或正在从其发送的远程主机的SocketAddress
   
参数:无
    
返回值:Socket类型
    
实例:

getData()

java
作用:获取数据包中的数据
   
参数:无
    
返回值:byte[]
    
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);
// 准备DatagramPacket对象
DatagramSocket socket = new DatagramSocket();
// 发送数据
socket.send(packet);
// 客户端接收数据
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();

DatagramSocket类

用于UDP创建Socket对象

构造方法

DatagramSocket()

java
作用:构造数据报套接字并将其绑定到本地主机上的任何可用端口
    
参数:无
    
返回值:DatagramSocket类型对象
    
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket();

DatagramSocket(int port, InetAddress laddr)

java
作用:创建一个数据报套接字,绑定到指定的本地地址

参数:端口号,地址
    
返回值:DatagramSocket类型对象
    
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455,localhost);

DatagramSocket(int port)

java
作用:构造数据报套接字并将其绑定到本地主机上的指定端口

参数:端口号
    
返回值:DatagramSocket类型对象
    
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455);

方法

receive(DatagramPacket p)

java
作用:从此套接字接收数据报包
    
参数:DatagramPacket类型对象
    
返回值:空
    
实例:
DatagramSocket socket = new DatagramSocket(12455);
System.out.println("服务已启动");
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);

// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 解析数据
String msg = new String(data,0,data.length);
System.out.println(msg);

send()

java
作用:从此套接字发送数据报包
    
参数:DatagramPacket p
    
返回值:空
    
实例:
// 创建Socket对象
DatagramSocket socket = new DatagramSocket(12455);
// 发送数据
byte[] sendData = "你好,客户端".getBytes();
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData, sendData.length,clientAddress,clientPort);
socket.send(packet);

InetAddress类

表示IP协议地址

构造方法

java

方法

getByAddress(byte[] addr)(静态方法)

java
作用:从此套接字接收数据报包(静态方法)
    
参数:byte类型数组
    
返回值:InetAddress对象
    
实例:
// 获取地址对象 使用InetAddress类静态方法
byte[] address = "127.0.0.1".getBytes();
InetAddress localhost = InetAddress.getByAddress(address);

getByName(String host)(静态方法)

java
作用:确定主机名称的IP地址(静态方法)
    
参数:String类型名称
    
返回值:InetAddress对象
    
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");

getPort()

java
作用:获取端口号(实例方法)
    
参数:String类型名称
    
返回值:InetAddress对象
    
实例:
// 创建Socket对象
DatagramSocket socket = new DatagramSocket(12455);
System.out.println("服务已启动");
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 获取客户端的IP信息
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();

TCP协议

TCP协议:面向连接的 安全的 可靠的传输协议;

单客户端(单线程、IO流)的TCP通信示例:

java
package com.NetProgram.Demo;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {

        try {
            // 创建服务器端Socket对象 端口号为 8899
            ServerSocket server = new ServerSocket(8899);
            System.out.println("服务器已启动");
            // 接收客户端请求 获取到Socket对象
            // accept()方法会等待用户发送请求 然后再继续向下执行
            Socket socket = server.accept();
            // 根据Socket对象获取字节读取流
            InputStream inputStream = socket.getInputStream();
            // 根据字节读取流创建 BufferedReader对象
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 读取对象
            System.out.println("接收到客户端信息:" + reader.readLine());

            // 发送信息
            OutputStream outputStream = socket.getOutputStream();
            String msg = "你好,客户端";
            outputStream.write(msg.getBytes());
            // 关闭流
            socket.shutdownOutput();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
java
package com.NetProgram.Demo;

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {

        try {
            // 创建客户端Socket对象 指定主机地址为本机 端口号为8899
            Socket socket = new Socket("127.0.0.1",8899);
            // 根据Socket对象获取到字节输出流
            OutputStream outputStream = socket.getOutputStream();
            String message = "你好,服务器";
            // 写入信息
            outputStream.write(message.getBytes());
            // 关闭写入流
            socket.shutdownOutput();

            // 读取数据
            InputStream inputStream = socket.getInputStream();
            // 创建带缓存读取流
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 打印
            System.out.println("服务器回复:" + reader.readLine());

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

多客户端(多线程、IO流)的TCP通信示例:

java
package com.NetProgram.Demo2;

import java.io.*;
import java.net.Socket;

public class Client2 {
    public static void main(String[] args) {
        // 创建 Socket对象
        try {
            Socket socket = new Socket("localhost",9090);
            // 获取一个写入流对象
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("你好,服务器".getBytes());
            // 关闭写入流
            socket.shutdownOutput();

            // 读取流
            InputStream inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 打印数据
            System.out.println("服务器回复:" + reader.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
java
package com.NetProgram.Demo2;

import java.io.*;
import java.net.Socket;

public class Client1 {
    public static void main(String[] args) {
        // 创建 Socket对象
        try {
            Socket socket = new Socket("127.0.0.1",9090);
            // 获取一个写入流对象
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("你好,服务器".getBytes());
            // 关闭写入流
            socket.shutdownOutput();

            // 读取流
            InputStream inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 打印数据
            System.out.println("服务器回复:" + reader.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
java
package com.NetProgram.Demo2;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        // 创建Socket
        try {
            ServerSocket server = new ServerSocket(9090);
            System.out.println("服务器已启动");
            // 一直运行
            while(true){
                Socket socket = server.accept();
                // 每一个socket,开启一个线程
                ServerThread serverThread = new ServerThread(socket);
                serverThread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
java
package com.NetProgram.Demo2;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class ServerThread extends Thread{
    // 定义属性为 套接字 socket
    private Socket socket;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // 服务器运行的代码,多线程
        InputStream inputStream = null;
        try {
            // 获取 读取流对象
            inputStream = socket.getInputStream();
            // 创建带缓存读取流
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            // 获取客户端的信息
            InetAddress clientInetInfo = socket.getInetAddress();
            String hostName = clientInetInfo.getHostName();
            String hostAddress = clientInetInfo.getHostAddress();
            // 打印信息
            System.out.println("hostName: " + hostName + "|" + reader.readLine());

            // 获取写入流
            OutputStream outputStream = socket.getOutputStream();
            // 写入数据
            outputStream.write("我是服务器,我一直在...".getBytes());
            // 关闭写入流
            socket.shutdownOutput();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用TCP协议 实现 多个客户端连接同一个服务器;

分析:多个客户端属于多个线程 请求同一个服务器 服务器也应该使用不同的线程来处理不同的客户端;

即 每次接收一个新的请求 就分配一个新的线程来处理对应的客户端;

TCP协议三次握手四次挥手

TCP协议的三次握手:

​ 第一次:客户端向服务器发送连接请求;

​ 第二次:服务器向客户端响应连接请求;

​ 第三次:客户端与服务器建立连接;

image-20250813110054392

TCP协议的四次挥手:

​ 第一次:客户端向服务器发送断开连接请求

​ 第二次:服务器向客服端响应收到断开连接请求(因为TCP连接是双向的,所以此时服务器依然可以 向客户端发送信息)

​ 第三次:客户端等待服务器发送信息完成,向服务器确定全部信息发送完毕,并且断开客户端与服务器的连接

​ 第四次:服务器向客户端断开连接

image-20250813110117119

UDP协议

UDP 用户数据报协议 非面向连接的 不安全 不可靠的传输协议 效率高;

Server端

java
package com.NetProgram.Demo3;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class Server {
    public static void main(String[] args) {

        try {
            // 创建Socket对象
            DatagramSocket socket = new DatagramSocket(12455);
            System.out.println("服务已启动");
            // 准备数组接收数据
            byte[] receiveData = new byte[36];
            // 准备数据包对象
            DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
            // 接收数据
            socket.receive(receivePacket);
            // 调用方法获取 获取接收到的数据
            byte[] data = receivePacket.getData();
            // 获取客户端的IP信息
            InetAddress clientAddress = receivePacket.getAddress();
            int clientPort = receivePacket.getPort();
            // 解析数据
            String msg = new String(data,0,data.length);
            System.out.println(msg);
            // 发送数据
            byte[] sendData = "你好,客户端".getBytes();
            // 准备数据包
            DatagramPacket packet = new DatagramPacket(sendData, sendData.length,clientAddress,clientPort);
            socket.send(packet);

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Client端

java
package com.NetProgram.Demo3;

import java.io.IOException;
import java.net.*;

public class Client {
    public static void main(String[] args) {
        // 准备数据
        String msg = "你好,服务器";
        byte[] sendData = msg.getBytes();
        try {
            // 获取地址对象 使用InetAddress类静态方法
            InetAddress localhost = InetAddress.getByName("127.0.0.1");
            // 准备数据包
            DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);
            // 准备DatagramPacket对象
            DatagramSocket socket = new DatagramSocket();
            // 发送数据
            socket.send(packet);
            // 客户端接收数据
            // 准备数组接收数据
            byte[] receiveData = new byte[36];
            // 准备数据包对象
            DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
            // 接收数据
            socket.receive(receivePacket);
            // 调用方法获取 获取接收到的数据
            byte[] data = receivePacket.getData();
            // 解析数据
            String res = new String(data,0,data.length);
            System.out.println(res);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}