Socket编程:客户端与服务器通信全解析(网络编程)

客⼾端和服务端

服务端:在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。

客⼾端:获取服务的⼀⽅进程,称为客⼾端。

Socket套接字

概念

Socket套接字,是由系统提供⽤于⽹络通信的技术,是基于TCP/IP协议的⽹络通信的基本操作单元。基于Socket套接字的⽹络程序开发就是⽹络编程。

socket 是操作系统中特殊的文件,表示"网卡"硬件设备

socket api 传输层,提供给应用层

TCP : 有链接, 可靠传输, 面向字节流, 全双工

UDP : 无链接, 不可靠传输, 面向数据报, 全双工

**链接:**抽象的,虚拟的链接.通信双方,各自保存对方的关键信息(IP和端口)

TCP 有链接,先保存对方的关键信息,然后发送数据

UDP 无链接,不需要保存对方的关键信息,直接发送数据

**不可靠传输:**数据发送出去之后,关系对方是否收到,如果

**可靠传输:**发送出去之后,不管了

**全双工:**同时进行双向通信

半双工: 同一时刻,只能单向通信**(兽医)**

UDP数据报套接字编程

DatagramSocket

Datagram:数据报

创建一个这样的对象,就相当于在系统内核里创建了一个socket文件 . 创建socket文件,也会占用文件描述符表的一个位置,用完之后要记得及时关闭,否则可能会出现**"文件资源泄露"**

DatagramSocket 构造方法

|--------------------------|----------------------------------------------|
| DatagramSocket() | 创建⼀个UDP数据报套接字的Socket,绑定到本机任 意⼀个随机端⼝(⼀般⽤于客⼾端) |
| DatagramSocket(int port) | 创建⼀个UDP数据报套接字的Socket,绑定到本机指 定的端⼝(⼀般⽤于服务端) |

谁主动,谁就是客户端. 谁被动,谁就是服务器.

DatagramSocket 方法

|--------------------------------|--------------------------------------|
| void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该 ⽅法会阻塞等待) |
| void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发 送) |
| void close() | 关闭此数据报套接字 |

DatagramPacket

DatagramPacket 构造方法

|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| DatagramPacket(byte\[\] buf, int length) | 构造⼀个DatagramPacket以⽤来接收数据报,接收的 数据保存在字节数组(第⼀个参数buf)中,接收指定 ⻓度(第⼆个参数length) |
| DatagramPacket(byte\[\] buf, int offset, int length, SocketAddress address) | 构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号 |

DatagramPacket ⽅法:

|--------------------------|--------------------------------------------|
| InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
| int getPort() | 从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号 |
| byte\[\] getData() | 获取数据报中的数据 |

InetSocketAddress : 创建⼀个Socket地址,包含IP地址和端⼝号

代码示例

java 复制代码
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    //先创建 socket对象
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        //启动服务器
        System.out.println("服务器启动....");

        //服务器,需要不断处理客户发送过来的请求
        while(true){
            //循环一次就是处理一个请求
            //1.读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(requestPacket);
            //为了处理方便
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.根据请求构造响应
            String response = process(request);
            //3.把响应返回给客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            System.out.printf("[%s:%d]; resp: %s\n",requestPacket.getAddress().toString(),responsePacket.getPort(),request,response);
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        //端口号是一个整数,随意指定
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}
java 复制代码
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoCient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoCient(String serverIp,int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIP = serverIp;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner sc = new Scanner(System.in);
        while (true) {
            //1.从控制台读取用户输入
            System.out.println("-> ");
            String request = sc.next();

            //2.构造请求,发送给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), this.serverPort);
            socket.send(requestPacket);
            //3.读取服务器响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            socket.receive(responsePacket);
            String response = new String(requestPacket.getData(), 0, requestPacket.getLength());

            System.out.println(response);

        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoCient cient = new UdpEchoCient("127.0.0.1",9090);
        cient.start();
    }
}

**服务器必须要指定固定的端口,**确保客户端随时能找到服务器.

客户端来说,必须不指定固定端口 ,让系统随机分配空闲的端口,避免端口号冲突**(大饼)**

相关推荐
中才实用1 小时前
Jenkins自动化部署
运维·自动化·jenkins
snow@li1 小时前
Kibana:功能全景梳理/Elastic Stack可视化管理平台
运维
AC赳赳老秦1 小时前
官方技术文档聚合实践:用 OpenClaw 批量抓取开源项目文档,构建离线可检索技术知识库
java·运维·服务器·python·信息可视化·deepseek·openclaw
fengkai45451 小时前
Linux服务--5.Nginx 服务
linux·运维·服务器
牛肉胡辣汤1 小时前
开源模型落地实战|开发、运维、安全各岗位 AI 应用经验分享
运维·安全·开源
数据知道1 小时前
网络安全实战:API 安全攻防——RESTful 接口渗透测试方法论
网络·安全·web安全·网络安全·restful
布局呆星10 小时前
云服务器上部署单项目/多项目
运维·服务器
盐焗鹌鹑蛋10 小时前
【Linux】缓冲区
linux·运维·服务器
祈禾11 小时前
Redis三大缓存问题与分布式锁
运维·数据库·redis·笔记·分布式·缓存