首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TCP和UDP

TCP和UDP

作者头像
用户11319080
发布2025-05-26 08:34:06
发布2025-05-26 08:34:06
10300
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

1.TCP和UDP的区别

这样理解比较难以理解:我们做个比喻。 TCP好比是打电话,UDP好比是发短信: 打电话时候必须双方确认,才能进行通话,发短信时候是不用接收方同意的,直接发送就行了。所有TCP是有连接,UDP是无连接的。为什么TCP是可靠传输,UDP是不可靠传输,因为打电话时候,必须双方确认才能进行通信,也保证了对方接收到了你的信息,发短信时候,我们并不会知道对方是否收到这条消息,打电话时候是你说一句,我说一句,可以一条一条来说,这就类似于面向字节流,发短信是一次编辑完整个要说的内容,然后发送类似于面向数据包,打电话是不限制我们时间的,而发短信会限制我们的字数多少。

2.UDP的使用场景(简单叙述)和UDP编程

1.使用场景:

一般使用在一些数据不是很重要的场景,可以丢失一些数据,举个不是很恰当的例子:比如共享单车的定位数据, 其实比较重要的是起始位置的日志数据和终点的日志数据,关于骑行中的日志数据,可能就显得不那么重要,这种情况其实可以使用UDP。像微信聊天这种,每条数据都很重要就不能使用UDP。

2.网络编程:

定义接收端和服务端的: DatagramSocket的构造方法:

普通方法:接收数据和发送数据

DatagramPacket(数据报) DatagramPacket是UDP Socket发送和接收的数据报 类似于打包数据的东西: 构造方法:

普通方法:

主要是用于记录客户端的地址和端口,为服务器返回数据提供客户端地址。

1:回显服务器的实现。

接收端实现:

代码语言:javascript
代码运行次数:0
运行
复制
package network;/*
 *@ 代阳敲的专属代码
 **/

import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class UdpEchoServer {
    //定义个DatagramSocket的引用
    private DatagramSocket socket;
    public UdpEchoServer(int port) throws SocketException {
        //创建一个服务器,port指定端口
        this.socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务端已经启动");
        //循环24小时处理客户端消息
        while (true) {
            //定义打包数据
            DatagramPacket requestpacket=new DatagramPacket(new byte[1024],1024);
            //接收客户端数据
            socket.receive(requestpacket);

            //转化为String类型,这个只适合文本数据,不可以是图片数据,图片数据转化为二进制。
            String request=new String(requestpacket.getData(),0,
                    requestpacket.getLength(),"UTF-8");

            //处理数据,并把反馈数据赋给response
            String response=process(request);

            //重新打包数据
            DatagramPacket responedPacket =new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),
                    0, response.getBytes().length,requestpacket.getSocketAddress());

            //发送给客户端
            socket.send(responedPacket);
            System.out.printf("[%s:%d] req: %s,resp: %s\n",requestpacket.getAddress().toString(),requestpacket.getPort(),request,response);
        }
    }
    //具体处理客户端数据的逻辑
    protected String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer=new UdpEchoServer(8080);
        udpEchoServer.start();
//        UdpEchoClient udpEchoClient=new UdpEchoClient("127.0.0.1",8080);
//        udpEchoClient.strat();
    }
}

客户端实现:

代码语言:javascript
代码运行次数:0
运行
复制
package network;/*
 *@ 代阳敲的专属代码
 **/

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Scanner;

public class  UdpEchoClient {
    
    private DatagramSocket socket;
    //客户端要发给那个服务端的IP
    private String sererIp;
    //客户端要发给那个服务端的端口
    private int serverPort;

    
    //创建客户端,并指定要发那个服务端的IP和端口
    public UdpEchoClient( String sererIp, int serverPort) throws SocketException {
        this.sererIp = sererIp;
        this.serverPort = serverPort;
        this.socket=new DatagramSocket();

    }
    public  void  strat() throws IOException {
        Scanner scanner =new Scanner(System.in);
        System.out.println("客户端启动");
        while(true) {
            System.out.println("->");
            String request=scanner.next();
            //打包客户端要发送的数据,参数(转化数据,从那开始,结束长度,服务端的地址)
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),0,request.getBytes().length,
                    new InetSocketAddress(sererIp,serverPort));
            //发送
            socket.send(requestPacket);
            
            //打包接收服务端的传输的数据
            DatagramPacket responsePacket =new DatagramPacket(new byte[1024],1024);
            //接收服务端的数据
            socket.receive(responsePacket);
            String response =new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);

        }
    }
    public static void main(String[] args) throws IOException {
//        UdpEchoServer udpEchoServer=new UdpEchoServer(8080);
//        udpEchoServer.start();
        UdpEchoClient udpEchoClient=new UdpEchoClient("127.0.0.1",8080);
        udpEchoClient.strat();
    }
}

效果:

2升级一下,变为一个字典:

其实基本逻辑是一样的,我们只需要改造返回逻辑就行了。

代码语言:javascript
代码运行次数:0
运行
复制
package network;/*
 *@ 代阳敲的专属代码
 **/

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

public class UdpDictServer extends UdpEchoServer{
    private Map<String,String> dict =new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        dict.put("cat", "⼩猫");
        dict.put("dog", "⼩狗");
        dict.put("fuck", "卧槽");
    }
    //修改处理数据逻辑
    @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();
    }
}

效果:

借助网上一张图再次讲述逻辑:哈哈哈,我感觉他讲的真的很详细

3.TCP的使用场景(简单叙述)和TCP的实现

1:使用场景

微信这种重要数据就必须全部保证传输到位,不能有一点丢失,而且必须建立连接

2:网络编程

1:回显服务器实现:

服务端

代码语言:javascript
代码运行次数:0
运行
复制
package network;/*
 *@ 代阳敲的专属代码
 **/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoService {
    private ServerSocket socket;
    // 这个操作就会绑定端⼝号
    public  TcpEchoService(int port) throws IOException {
        if(1024>port||port>65535) {
            throw new BindException("端口不合法");
        }
        this.socket=new ServerSocket(port);
    }
    // 启动服务器
    public void  start() throws IOException {
        System.out.println("服务器启动");
        while(true) {
            Socket clienSocket =socket.accept();
            processConnection(clienSocket);
        }
    }
    // 通过这个⽅法来处理⼀个连接的逻辑
    private void processConnection(Socket clienSocket) {
        System.out.printf("[%s:%d] 客户端上线!\n",clienSocket.getInetAddress().toString(),clienSocket.getPort());
        try {
            InputStream inputStream=clienSocket.getInputStream();
            OutputStream outputStream=clienSocket.getOutputStream();
            // ⼀次连接中, 可能会涉及到多次请求/响应
            while(true) {
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    // 读取完毕, 客⼾端下线
                    System.out.printf("[%s:%d] 客⼾端下线!\n" ,clienSocket.getInetAddress(),clienSocket.getPort());
                    break;
                }
                //
                String request=scanner.next();
                // 2. 根据请求计算响应
                String response = process(request);
                //把数据返回给客户端,用Prwinter包裹一层
                PrintWriter writer=new PrintWriter(outputStream);
                writer.println(response);
                writer.flush();
                System.out.printf("[%s:%d] req: %s, resp: %s\n",
                        clienSocket.getInetAddress(),clienSocket.getPort(),request,response);

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            // 在 finally 中加上 close 操作, 确保当前 socket 被及时关闭!!
            try {
                clienSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

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

    public static void main(String[] args) throws IOException {
        TcpEchoService server =new TcpEchoService(8080);
        server.start();
    }
}

客户端

代码语言:javascript
代码运行次数:0
运行
复制
package network;/*
 *@ 代阳敲的专属代码
 **/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    Socket socket;

    //和服务器通信,就要知道服务器的ip和端口
    public TcpEchoClient(String serverIp,int port) throws IOException {
        socket=new Socket(serverIp,port);
    }
    public void start() {
        System.out.println("客户端启动");
        Scanner scannerConsle =new Scanner(System.in);


        try {
            InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream();
            while(true) {
                //从控制台输入字符
                System.out.println("->");
                String request=scannerConsle.next();
                //把请求发送给服务端
                PrintWriter printWriter=new PrintWriter(outputStream);

                printWriter.println(request);
                //刷新缓存区
                printWriter.flush();
                //从服务端返回数据
                Scanner sannerNetwork =new Scanner(inputStream);
                String response= sannerNetwork.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",8080);
        client.start();
    }
}

实现的效果和UDP是类似的。

2:多个客户端的问题。

我们多对一的时候发现,其中一个客户端没有得到服务器的响应这是为什么莫呢? 原因如下:

这是因为我们第一个客户端建立连接后,进入process方法后,一直处于循环状态,导致第二个客户端没办法让和服务端建立连接。

我们可以用多线程和线程池解决这个问题。

多线程:

线程池:

4. TCP和UDP的服务端和客户端连接的不同体现(网络上体现)

我们会发现,UDP是不显示服务端和客户端连接的信息,而TCP是会显示的。所以这也证明了,UDP是不连接的,TCP是连接的。

5.C10K问题

问题:假设现在有100万个客户同时访问服务器,与服务器建立连接,那么服务器就要创建100万个线程,那么就会造成服务器资源耗尽,最终崩溃,面对这种情况我们如果解决?

以我现在了解的知识是用线程池解决。

我们首先了解一下,线程池的原理:

线程池有几个重要参数,核心线程,最大线程,阻塞队列,当我们创建的线程数达到核心线程数,就会再次创建的线程数放在阻塞队列里面,然后当阻塞队列也满了的话,就会创建临时线程,当达到最大线程数时候,再有客户端访问,就会直接拒绝掉,这也保证了服务器不会无限制创建线程,导致资源耗尽服务器崩溃。

6.长连接和短连接

TCP发送数据时候,都需要建立连接,什么时候关闭决定着是长连接或者短连接

短连接:建立连接后,客户端发送一次消息后,并接收到服务器的响应后,必须关闭的连接就是短连接。 长连接:建立连接后,客户端发送一次消息后,并接收到服务器的响应后,不需要关闭连接,保持长时间连接的就是长连接。

场景:短连接一般是客户端发送给服务端请求,类似于访问浏览器。客服会话。 长连接一般是客户端请求服务端,也可以是服务端给客户端发送请求。比如聊天室,实施游戏。

扩展:BIO(同步阻塞IO)和NIO(同步非阻塞IO) 基于BIO的长连接,会一直占用系统资源,一直会占用一个线程,阻塞等待接收消息,对于高并发场景,这样会消耗巨大的资源,服务器是撑不住的,比如一百万个人同时基于BIO的长连接,那么就要有一百万个线程被创建,持续阻塞。

NIO是用一个线程处理多个连接,这样就提高效率,也缓解了服务器的压力。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.TCP和UDP的区别
  • 2.UDP的使用场景(简单叙述)和UDP编程
    • 1.使用场景:
    • 2.网络编程:
      • 1:回显服务器的实现。
      • 2升级一下,变为一个字典:
  • 3.TCP的使用场景(简单叙述)和TCP的实现
    • 1:使用场景
    • 2:网络编程
      • 1:回显服务器实现:
      • 2:多个客户端的问题。
  • 4. TCP和UDP的服务端和客户端连接的不同体现(网络上体现)
  • 5.C10K问题
  • 6.长连接和短连接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档