简单实现基于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

相关文章
|
1月前
|
网络协议 Java API
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
62 2
|
1月前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
55 2
|
1月前
|
存储 网络协议 Java
【网络】UDP和TCP之间的差别和回显服务器
【网络】UDP和TCP之间的差别和回显服务器
65 1
|
2月前
|
存储 网络协议 算法
UDP 协议和 TCP 协议
本文介绍了UDP和TCP协议的基本结构与特性。UDP协议具有简单的报文结构,包括报头和载荷,报头由源端口、目的端口、报文长度和校验和组成。UDP使用CRC校验和来检测传输错误。相比之下,TCP协议提供更可靠的传输服务,其结构复杂,包含序列号、确认序号和标志位等字段。TCP通过确认应答和超时重传来保证数据传输的可靠性,并采用三次握手建立连接,四次挥手断开连接,确保通信的稳定性和完整性。
93 1
UDP 协议和 TCP 协议
|
3月前
|
消息中间件 网络协议 算法
UDP 和 TCP 哪个更好?
【8月更文挑战第23天】
212 0
|
27天前
|
网络协议 算法 网络性能优化
|
15天前
|
网络协议 SEO
TCP连接管理与UDP协议IP协议与ethernet协议
TCP、UDP、IP和Ethernet协议是网络通信的基石,各自负责不同的功能和层次。TCP通过三次握手和四次挥手实现可靠的连接管理,适用于需要数据完整性的场景;UDP提供不可靠的传输服务,适用于低延迟要求的实时通信;IP协议负责数据包的寻址和路由,是网络层的重要协议;Ethernet协议定义了局域网的数据帧传输方式,广泛应用于局域网设备之间的通信。理解这些协议的工作原理和应用场景,有助于设计和维护高效可靠的网络系统。
26 4
|
21天前
|
缓存 负载均衡 网络协议
面试:TCP、UDP如何解决丢包问题
TCP、UDP如何解决丢包问题。TCP:基于数据块传输/数据分片、对失序数据包重新排序以及去重、流量控制(滑动窗口)、拥塞控制、自主重传ARQ;UDP:程序执行后马上开始监听、控制报文大小、每个分割块的长度小于MTU
|
1月前
|
网络协议 前端开发 物联网
TCP和UDP区别?
本文首发于微信公众号“前端徐徐”,详细介绍了TCP和UDP两种传输层协议的核心概念、连接性和握手过程、数据传输和可靠性、延迟和效率、应用场景及头部开销。TCP面向连接、可靠、有序,适用于网页浏览、文件传输等;UDP无连接、低延迟、高效,适用于实时音视频传输、在线游戏等。
49 1
TCP和UDP区别?
|
29天前
|
Web App开发 缓存 网络协议
不为人知的网络编程(十八):UDP比TCP高效?还真不一定!
熟悉网络编程的(尤其搞实时音视频聊天技术的)同学们都有个约定俗成的主观论调,一提起UDP和TCP,马上想到的是UDP没有TCP可靠,但UDP肯定比TCP高效。说到UDP比TCP高效,理由是什么呢?事实真是这样吗?跟着本文咱们一探究竟!
53 10
下一篇
无影云桌面