简单实现基于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月前
|
域名解析 网络协议 关系型数据库
tcp和udp的区别是什么
TCP和UDP是互联网协议中的传输层协议。TCP是面向连接的,通过三次握手建立可靠连接,提供数据顺序和可靠性保证,适用于HTTP、FTP等需要保证数据完整性的应用。UDP则是无连接的,数据报独立发送,传输速度快但不保证可靠性,常用于实时通信、流媒体和DNS解析等对速度要求高的场景。根据应用需求选择合适的协议至关重要。
tcp和udp的区别是什么
|
1月前
|
网络协议 网络性能优化
认识TCP和UDP的区别
重排机制:由于UDP数据包可能因网络原因而发生乱序,因此在应用层需要对接收到的数据包进行排序。
34 4
|
4天前
|
网络协议 安全 网络性能优化
CCNA 200-301系列:TCP和UDP简介
【4月更文挑战第21天】
17 8
|
4天前
|
缓存 网络协议 安全
TCP和UDP 传输层协议的区别
TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。
10 2
|
6天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
22天前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
1月前
|
网络协议 网络性能优化
网络面试题:TCP和UDP的区别
网络面试题:TCP和UDP的区别
25 0
|
1月前
|
网络协议 Python
Python网络编程实现TCP和UDP连接
Python网络编程实现TCP和UDP连接
31 0
|
1月前
|
网络协议 网络性能优化 Python
python怎么实现tcp和udp连接
python怎么实现tcp和udp连接
16 0
|
1月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【2月更文挑战第19天】
TCP 和 UDP 的 Socket 调用

热门文章

最新文章