简单实现基于UDP与TCP的回显服务器(一)

简介: 简单实现基于UDP与TCP的回显服务器

前言


我们写网络程序, 主要编写的是应用层代码.

真正要发送这个数据, 还需要上层协议调用下层协议, 也就是应用层调用传输层.

传输层给应用层提供了一组 api, 统称为 socket api, 系统给程序提供的 api 是C 风格的, JDK 针对这些 api 进行封装, 封装成 Java 风格的 api.


提供的 socket api 主要是这两组 :


  1. 基于 UDP 的 api
  2. 基于 TCP 的 api


因为 UDP 与 TCP 的协议差别很大, 所以这两组 api 差别也很大.


那这两个协议都有啥特点呢?


UDP :


无连接 (使用 UDP 的双方不需要刻意保存对端的相关信息)

不可靠传输 (消息发送完了就行, 不关注结果)

面向数据报 (以一个 UDP 数据报为基本单位)

全双工 (一条路径, 双向通信)


TCP :


有连接 (使用 TCP 的双方要刻意保存对端的相关信息)

可靠传输 (发送消息后, 知道对方是否接收到)

面向字节流 (以字节为传输的基本单位, 读写方式非常灵活)

全双工 (一条路径, 双向通信)


UDP 版的回显服务器


需要用到的 api


1.DatagramSocket API


DatagramSocket 是 UDP Socket,用于发送和接收UDP数据报。

主要用到的构造方法 :


DatagramSocket()


创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)


DatagramSocket(intport)


创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)


主要用到的方法 :


void receive(DatagramPacket p)


从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)


void send(DatagramPacketp)


从此套接字发送数据报包(不会阻塞等待,直接发送)


void close()


关闭此数据报套接字


2.DatagramPacket API


DatagramPacket是UDP Socket发送和接收的数据报.

主要用到的构造方法 :


DatagramPacket(byte[] buf, int length)


构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)


DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)


构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从offset到指定长度(第三个参数length)。address指定目的主机的IP和端口号


主要用到的方法 :


InetAddress getAddress()


从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址.


int getPort()


从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获

取接收端主机端口号


byte[] getData()


获取数据报中的数据


构造UDP发送数据报时,需要传入 SocketAddress ,该对象可以使用InetSocketAddress 来创建.


服务端


public class UdpEchoServer {
    //首先定义一个 socket 对象, 通过 socket 对象来发送读取信息
    private DatagramSocket socket = null;
    //绑定一个端口, 如果绑定的端口被别的进程占用了, 这里就会报错.
    //同一个主机上, 一个端口只能被一个进程绑定.
    public UdpEchoServer(int port) throws SocketException {
        //构造时, 指定要绑定的端口号.
        socket = new DatagramSocket(port);
    }
    //启动服务器的主逻辑
    public void start() throws IOException {
        System.out.println("服务器启动!!!");
        //因为服务器是要时刻读取客户端信息的, 所以使用while循环来重复读取并处理信息.
        while(true) {
            //每次循环都只做三件事
            // 1.读取请求并解析
            //   下面这是构造一个数据包, 就可以理解为一个餐盘, 这个餐盘里指定要放入的数据类型及大小
            //   空的餐盘构建好了就得装东西了
            DatagramPacket requestPacket = new DatagramPacket(new byte[666], 666); 
            //   1kb=1024byte, UDP最多发送64kb(包含UDP首部8byte)
            //   通过 socket 对象来接收信息(也就是填充餐盘)
            socket.receive(requestPacket); //如果没接收到信息就会阻塞等待
            //   为了方便我们处理这个请求, 将数据包转为 String
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());
            // 2.根据请求来计算响应(这里直接返回了)
            String response = process(request);
            // 3.把响应结果写回客户端
            //   根据 response 来构造一个 DatagramPacket
            //   和之前构造不同, 本次构造相当于将餐盘填满, 注意还要指定发送给谁
            //   requestPacket.getSocketAddress()是获取客户端的IP和端口号(填充的 requestPacket 就包含了客户端的IP和地址)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    0,response.getBytes().length,requestPacket.getSocketAddress());
            //   把结果发送给客户端
            socket.send(responsePacket);
            //打印一下客户端的IP和端口号, 还有请求和响应
            System.out.printf("[%s : %d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }
    //根据请求计算响应
    public String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoServer echoServer = new UdpEchoServer(8888);
        echoServer.start();
    }
}


客户端


public class UdpEchoClient {
    //客户端也需要 socket 对象来进行数据交互
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    //构造客户端时, 还需要知道服务器在哪, 才能去获取服务
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //不同与服务器, 这里没有关联端口, 不代表不需要关联端口
        //而是系统会自动为客户端分配个空闲的端口
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("启动客户端!!!");
        //通过 Scanner 来读取用户输入
        Scanner scanner = new Scanner(System.in);
        while(true) {
            // 1.先从控制台读取一个字符串
            //   打印一个提示符, 提示用户输入
            System.out.print("-> ");
            String request = scanner.next();
            // 2.把字符串构造成 UDP packet, 并进行发送
            //   InetAddress.getByName() 确定主机的IP地址
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            // 3.客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[666],666);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request,response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",8888);
        udpEchoClient.start();
    }
}



启动看看效果 :


客户端 :


1edf9156d60c4cbbbd14812a96984026.png

服务端 :

b71395f2f12c442abc3c6949043bfce7.png


UDP 版本的字典客户端和字典服务器


要想实现单词查找功能, 首先要有一个数据结构来对单词进行存储, 一般用哈希表来存储, key 来存储单词, value 存储单词意思.


其实客户端都是一样的, 只是服务器根据请求, 返回的响应不一样了.

所以我们主要就是重写 process 方法.


服务端 :


public class UdpDictServer extends UdpEchoServer{
    private HashMap<String,String> dict = new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        dict.put("pen","笔");
        dict.put("dog","狗");
        dict.put("cat","猫");
        //...可以无限添加, 其实网上的翻译词典什么的,就是样本比这个大
    }
    @Override
    public String process(String request) {
        return dict.getOrDefault(request,"未找到该单词.");
    }
    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer = new UdpDictServer(8888);
        udpDictServer.start();
    }
}

客户端 :

d3a6adc5b7534d76ab53abcc523d47e5.png


服务端 :


8f66d64a346a45e2abb92e1c21462b1c.png

相关文章
|
7天前
|
缓存 网络协议 Java
【JavaEE】——TCP回显服务器(万字长文超详细)
ServerSocket类,Socket类,PrintWriter缓冲区问题,Socket文件释放问题,多线程问题
|
10天前
|
监控 网络协议 网络性能优化
不再困惑!一文搞懂TCP与UDP的所有区别
本文介绍网络基础中TCP与UDP的区别及其应用场景。TCP是面向连接、可靠传输的协议,适用于HTTP、FTP等需要保证数据完整性的场景;UDP是无连接、不可靠但速度快的协议,适合DNS、RIP等对实时性要求高的应用。文章通过对比两者在连接方式、可靠性、速度、流量控制和数据包大小等方面的差异,帮助读者理解其各自特点与适用场景。
|
20天前
|
存储 网络协议 安全
用于 syslog 收集的协议:TCP、UDP、RELP
系统日志是从Linux/Unix设备及网络设备生成的日志,可通过syslog服务器集中管理。日志传输支持UDP、TCP和RELP协议。UDP无连接且不可靠,不推荐使用;TCP可靠,常用于rsyslog和syslog-ng;RELP提供可靠传输和反向确认。集中管理日志有助于故障排除和安全审计,EventLog Analyzer等工具可自动收集、解析和分析日志。
|
2月前
|
网络协议 网络性能优化 数据处理
深入解析:TCP与UDP的核心技术差异
在网络通信的世界里,TCP(传输控制协议)和UDP(用户数据报协议)是两种核心的传输层协议,它们在确保数据传输的可靠性、效率和实时性方面扮演着不同的角色。本文将深入探讨这两种协议的技术差异,并探讨它们在不同应用场景下的适用性。
71 4
|
2月前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
61 3
|
2月前
|
网络协议 SEO
TCP连接管理与UDP协议IP协议与ethernet协议
TCP、UDP、IP和Ethernet协议是网络通信的基石,各自负责不同的功能和层次。TCP通过三次握手和四次挥手实现可靠的连接管理,适用于需要数据完整性的场景;UDP提供不可靠的传输服务,适用于低延迟要求的实时通信;IP协议负责数据包的寻址和路由,是网络层的重要协议;Ethernet协议定义了局域网的数据帧传输方式,广泛应用于局域网设备之间的通信。理解这些协议的工作原理和应用场景,有助于设计和维护高效可靠的网络系统。
46 4
|
2天前
|
机器学习/深度学习 人工智能 PyTorch
阿里云GPU云服务器怎么样?产品优势、应用场景介绍与最新活动价格参考
阿里云GPU云服务器怎么样?阿里云GPU结合了GPU计算力与CPU计算力,主要应用于于深度学习、科学计算、图形可视化、视频处理多种应用场景,本文为您详细介绍阿里云GPU云服务器产品优势、应用场景以及最新活动价格。
阿里云GPU云服务器怎么样?产品优势、应用场景介绍与最新活动价格参考
|
1天前
|
存储 运维 安全
阿里云弹性裸金属服务器是什么?产品规格及适用场景介绍
阿里云服务器ECS包括众多产品,其中弹性裸金属服务器(ECS Bare Metal Server)是一种可弹性伸缩的高性能计算服务,计算性能与传统物理机无差别,具有安全物理隔离的特点。分钟级的交付周期将提供给您实时的业务响应能力,助力您的核心业务飞速成长。本文为大家详细介绍弹性裸金属服务器的特点、优势以及与云服务器的对比等内容。
|
9天前
|
人工智能 JSON Linux
利用阿里云GPU加速服务器实现pdf转换为markdown格式
随着AI模型的发展,GPU需求日益增长,尤其是个人学习和研究。直接购置硬件成本高且更新快,建议选择阿里云等提供的GPU加速型服务器。
利用阿里云GPU加速服务器实现pdf转换为markdown格式
|
8天前
|
开发框架 缓存 .NET
阿里云轻量应用服务器、经济型e、通用算力型u1实例怎么选?区别及选择参考
在阿里云目前的活动中,价格比较优惠的云服务器有轻量应用服务器2核2G3M带宽68元1年,经济型e实例2核2G3M带宽99元1年,通用算力型u1实例2核4G5M带宽199元1年,这几个云服务器是用户关注度最高的。有的新手用户由于是初次使用阿里云服务器,对于轻量应用服务器、经济型e、通用算力型u1实例的相关性能并不是很清楚,本文为大家做个简单的介绍和对比,以供参考。