首页 > 编程语言 >详解UDP-TCP网络编程

详解UDP-TCP网络编程

时间:2024-10-20 10:17:57浏览次数:9  
标签:UDP String TCP 连接 详解 new 客户端 public Socket

目录

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();
    }
}

标签:UDP,String,TCP,连接,详解,new,客户端,public,Socket
From: https://blog.csdn.net/wmh_1234567/article/details/141202462

相关文章

  • 详解Java之lambda
    目录lambda引入语法函数式接口lambda表达式的使用语法精简:代码示例:变量捕获局部变量捕获成员变量捕获lambda在集合中的使用lambda的优缺点lambda引入Lambda表达式是JavaSE8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达......
  • 树状数组——原理详解
    前言这两天在网上学树状数组,但是发现网上关于树状数组的解释大都对初学者不太友善,原理讲解部分并不是很容易理解,所以写了一篇树状数组,顺便帮自己巩固一下。一、什么是树状数组1.概念:简单来说,这是一种数据结构。顾名思义,它通过树的结构来对数组进行高效操作,一般用于求数组前缀......
  • Java之反射机制详解
    一、基本概念Java反射(Reflection)是一种允许程序在运行时动态地检查和操作类、接口、字段、方法等内部信息的机制。通过反射,程序可以在不知道对象类型的情况下创建对象、调用方法和访问字段,甚至访问私有成员。反射机制为Java程序提供了极大的灵活性和扩展性,是Java语言中一个......
  • UDP协议和TCP协议
    UDP协议:        是一种无连接的、简单的传输层通信协议,它在IP协议(网络层)之上提供服务。特点:无连接:在数据传输前,发送方和接收方之间不需要建立连接,可以直接发送数据。简单:UDP协议头只有8个字节,比TCP协议头简单,因此开销较小。不保证可靠性:UDP不提供数据传输的可......
  • Deepsort算法详解
    多目标跟踪的主要步骤:获取原视频帧利用目标检测器对视频帧中的目标进行检测将检测到的目标的框中的特征提取出来,该特征包括表观特征(方便特征对比避免IDswitch)和运动特征(运动特征方便卡尔曼滤波对其进行预测)表观特征与运动特征:表观特征:描述目标的外观信息,通常包括颜色、纹......
  • 2024/10/19日 日志--》关于MySQL中 JDBC的API 详解的整理简述
    今天进一步学习了JDBC中的API,已经可以初步连接数据库了,接下来继续进行学习。点击查看代码--JDBCAPI详解--DirverManager--DriverManager(驱动管理类)作用:1.注册驱动2.获取数据库连接--1.注册驱动--Class.forName("com.mysql.jdbc.Driver");--·需要注意的是:My......
  • USB协议详解第13讲(USB传输-控制传输及事务组成)
    1.前言前面讲过USB一个传输由多个事务组成,一个事务由多个包实体组成。传输又分为控制传输、同步传输、批量传输、中断传输四种,今天我们主要讲解控制传输三个阶段及事务组成。控制传输是一种特殊的传输方式,且传输过程相对复杂一些,但十分重要。当USB设备初次连接主机时,用控制传输......
  • USB协议详解第14讲(USB传输-同步传输及事务组成)
    1.前言前面讲过USB一个传输由多个事务组成,一个事务由多个包实体组成。传输又分为控制传输、同步传输、批量传输、中断传输四种,上一节我们讲了控制传输细节及事务组成,今天我们主要讲解同步传输及事务组成。同步传输用在数据量大、对实时性要求高的场合,例如音频设备、视频设备等,这......
  • 「图::连通」详解并查集并实现对应的功能 / 手撕数据结构(C++)
    目录概述成员变量创建销毁根节点访问路径压缩启发式合并复杂度Code概述并查集,故名思议,能合并、能查询的集合,在图的连通性问题和许多算法优化上着广泛的使用。这是一个什么数据结构呢?一般来讲,并查集是由一系列集合组成的集合群。其中,每个集合都有一个根节点,它的......
  • Linux C语言TCP协议实战
    文章目录1.TCP简介2.搭建框图3.相关函数介绍3.1socket函数3.2bind函数3.3listen函数3.4accept函数3.5connect函数3.6send函数3.7recv函数3.8其他函数4.实战4.1一对一模型4.1.1server.c4.1.2client.c4.1.3终端结果4.2多进程模型4.2.1server.c4.2.2cl......