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 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
AI职业加油站1 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
wuyk5551 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
此冬歌咏1 天前
K8s 节点故障实战:优雅驱逐 31 秒,硬故障 331 秒,以及那个永远 Pending 的 Pod
运维·k8s
-梅1 天前
linux(8) 软硬链接
linux·运维·服务器
Wang's Blog1 天前
Java 项目实战: 外卖平台-文件下载与ServletOutputStream回写浏览器
服务器·项目开发
张洛闻Eren1 天前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
琥珀色糖1 天前
SimlpeHttp
linux·服务器
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
第十人i1 天前
Linux 日志管理系统:journald 配置与优化
linux·服务器