当前位置: 首页 > article >正文

详解UDP-TCP网络编程

目录

UDP数据报套接字编程

API

代码示例--(回显)单个客户端

UdpEchoServer

UdpEchoClient

UdpDictServer(词典)

将服务端程序部署到云服务器上

TCP流套接字编程

API

长短链接

代码示例--(回显)多个客户端

TcpEchoServer

TcpEchoClient


UDP数据报套接字编程
API

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

DatagramSocket 构造方法:

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定端口(一般用于服务器)

ocket 方法:

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据包,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报(不会则色等待,直接发送)
void close()关闭此数据报套接字

DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:

DatagramPacket 方法:

方法签名方法说明
InetAddress getAddress()从接受的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接受的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机的端口号
byte[] getData()获取数据报中的数据

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

InetSocketAddress ( SocketAddress 的子类 )构造方法:

方法签名方法说明
InetSocketAddress(InetAddres addr,int port)创建一个Socket地址,包含IP地址和端口号

代码示例--(回显)单个客户端
UdpEchoServer
public class UdpEchoServer {
    //创建一个DatagramSocket对象,后续操作网卡
    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[4096],4096);
            socket.receive(requestPacket);
            //当前完成receive之后,数据是以二进制的形式存储到DatagramPacket中了
            //要想能够把这里的数据给显示出来,还需要把这个二进制数据转换成字符串
            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);
            //4.打印
            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 server=new UdpEchoServer(9090);
        server.start();
    }
}
UdpEchoClient
public class UdpEchoClient {
    private DatagramSocket socket=null;
    private String serverIp="";
    private int serverPort=0;

    public UdpEchoClient(String ip,int port) throws SocketException {
        //不能指定端口
        socket=new DatagramSocket();
        //由于Udp自身不会持有对端的信息,就需要把对端的情况给记录下来
        serverIp=ip;
        serverPort=port;
    }

    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner scanner=new Scanner(System.in);
        while(true){
            System.out.print("->");
            String request=scanner.next();
            //把请求内容构造成DatagramPacket对象,发送给服务器
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIp),serverPort);
            socket.send(requestPacket);
            //读取响应
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            //把响应转换成字符串
            String response=new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }


    public static void main(String[] args) throws IOException {
//        UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
        UdpEchoClient client=new UdpEchoClient("123.249.93.104",9090);
        client.start();
    }
}
UdpDictServer(词典)
public class UdpDictServer extends UdpEchoServer{
    private Map<String,String> dict=new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);

        dict.put("dog","小狗");
        dict.put("cat","小猫");
        dict.put("pig","小猪");
    }

    @Override
    public String process(String request){
        return dict.getOrDefault(request,"该词在字典中不存在");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server=new UdpDictServer(9090);
        server.start();
    }
}
将服务端程序部署到云服务器上

打 jar 包

将生成的 jar 包上传到云服务器上,然后使用命令 java -jar netComunicate.jar

TCP流套接字编程
API

ServerSocket 是创建TCP服务端Socket的API。
ServerSocket 构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
Socket 构造方法:

方法签名方法说明
Socket(String host,int port)创建一个客户端流套接字Socket,并与对应IP的主机上对应端口的进行建立连接

方法签名方法说明
InetAddress getInetAddress()返回套接字所建立的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

长短链接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。


对比以上长短连接,两者区别如下:

建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。 

代码示例--(回显)多个客户端
TcpEchoServer
public class TcpEchoServer {
    private ServerSocket serverSocket=null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService service= Executors.newCachedThreadPool();
        while(true){
            //通过accept方法,把内核中已经建立好的连接拿到应用程序中
            Socket clientSocket=serverSocket.accept();
//            Thread t=new Thread(()->{
//                processConnection(clientSocket);
//            });
//            t.start();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

    public void processConnection(Socket clientSocket){
        System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            //使用 try() 方式,避免后续用完了流对象,忘记关闭
            while(true){
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                //1.读取请求并解析,此处就以 next 作为读取请求的方法,next的规则就是读到"空白符"就返回
                String request=scanner.next();
                //2.根据请求,计算响应
                String response=process(request);
                //3.把响应写回客户端
                //  可以把String 转换成字节数组,写入到OutputStream
                //  也可以使用PrintWriter把OutputStream包裹一下,来写入字符串
                PrintWriter printWriter=new PrintWriter(outputStream);
                //  此处的println不是打印到控制台了,而是写入到outputStream对应的流对象中
                //  也就是写入到clientSocket里面,自然这个数据也就通过网络发送出去了
                //  此处使用println带有 \n 也是为了后续客户端这边可以使用 scanner.next来读取数据
                printWriter.println(response);
                //  此处还应该 刷新缓冲区,如果没有刷新缓冲区,可能数据仍然在内存中,没有被写入网卡
                printWriter.flush();
                //4.打印一下这次请求交互过程的内容
                System.out.printf("[%s:%d] req=%s, resp=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,response);
            }
        } catch (IOException e) {
//            throw new RuntimeException(e);
            e.printStackTrace();
        }finally {
            try{
                //在这个地方进行clientSocket的关闭
                //processConnection就是在处理一个链接,这个方法这行完毕,这个链接也就处理完了
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

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

    public static void main(String[] args) throws IOException {
        TcpEchoServer server=new TcpEchoServer(9090);
        server.start();
    }
}
TcpEchoClient
public class TcpEchoClient {
    private Socket socket=null;

    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        socket=new Socket(serverIp,serverPort);
    }

    public void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            PrintWriter writer=new PrintWriter(outputStream);
            Scanner scannerNetwork=new Scanner(inputStream);
            while(true){
                System.out.print("->");
                String request=scanner.next();
                //  这里使用 println 是为了让请求后面带上换行
                //  也就是和服务器读取请求 sacnner.next呼应
                writer.println(request);
                writer.flush();
                String response=scannerNetwork.next();
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

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

http://www.kler.cn/news/354699.html

相关文章:

  • 【C#生态园】提升数据处理效率:C#中多款数据清洗库全面解析
  • 【wpf】07 后端验证及令牌码获取步骤
  • [旧日谈]关于Qt的刷新事件频率,以及我们在Qt的框架上做实时的绘制操作时我们该关心什么。
  • 关于FFmpeg【使用方法、常见问题、解决方案等】
  • jmeter 对 dubbo 接口测试是怎么实现的?有哪几个步骤
  • 我谈结构自相似性SSIM——实质度量的是什么?
  • JavaScript 小技巧和诀窍:助你写出更简洁高效的代码
  • Scale Decoupled Distillation 论文中SPP发生了什么
  • 一款AutoXJS现代化美观的日志模块AxpLogger
  • k8s-配置网络策略 NetworkPolicy
  • docker/docker-compose里面Command和entrypoint的关系
  • 股票Tick数据如何获取做量化交易
  • springboot如何接入阿里云短信
  • Vue 3 中的状态管理:深入探讨 Vuex 和 Pinia 的比较与最佳实践
  • 初识git · 有关模型
  • 【C语言】数据类型
  • 实用篇:如何让Win11右键默认显示更多呢
  • STM32 独立看门狗和窗口看门狗区别
  • Python进阶知识
  • 智能平台或系统中的归因、根因分析案例集锦