网络编程Java Socket(UDP/TCP 套接字)

news2024/11/27 18:44:17

Socket是什么?
想知道Socket是什么就先得了解一下什么是网络编程
网络编程,通过代码来控制两个主机的进程之间能够进行数据交互。

操作系统就把网络编程的一些相关操作,封装起来了,提供了一组API供程序员使用。操作系统提供的功能,访问网络核心的硬件设备,网卡。网卡也是归操作系统来管理的

操作系统提供的socket api 是C语言风格的接口,在Java中是不能直接使用的。JDK其实也针对C语言这里的 socket API 进行了封装,在标准库中有一组类,这组类就能够让我们完成网络编程,这组类本质上仍然是调用的操作系统提供的socketAPI

操作系统,提供的 socket API主要有两类(实际上不止两类),它属于传输层
TCP/UDP
TCP和UDP这里只是简单说一下它们的特点,便于理解Socket编程
TCP

有连接
可靠传输
面向字节流
全双工
UDP

无连接
不可靠传输
面向数据报
全双工
有连接:类似于微信视频,需要接通才能说话
无连接:类似于发微信消息,直接发就好了
可靠传输:发送方能知道对方是否收到消息
不可靠传输:发送方不知道是不是收到了消息

注意:可靠性 != 安全性

面向字节流:
假设发送数据为1000个字节,可以一次性发10个字节重复发100次,也可以一次发100个字节,重复发送10次,可以非常灵活的完成这里的发送,接收也是同理
TCP的文件读写都是面向字节流的

面向数据报:
以一个一个的数据报为基本单位(每个数据报多大,不同的协议里面是有不同的约定的)
发送的时候,一次至少发送一个数据报,如果尝试发送一个半,实际可能只能发出去一个
接收的时候,一次至少接收一个数据,如果尝试接收半个,剩下半个就没了

全双工:双向通信,A和B可以同时向对方发送数据
半双工:单向通信,要么A给B发,要么B给A发,不能同时发
就类似于两根水管和一根水管的区别

套接字
一个服务器的核心流程

  1. 读取请求并解析
  2. 根据请求计算响应
  3. 把响应写回客户端

一个客户端的核心流程

  1. 根据用户输入,构造请求
  2. 发送请求给服务器
  3. 读取服务器的响应
  4. 解析响应并显示

UDP套接字
DatagramSocket API
DatagramSocket API 是UDP Socket,用于发送和接收UDP数据报

DatagramSocket构造方法
在这里插入图片描述
DatagramSocket 方法
在这里插入图片描述
构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

InetSocketAddress API
InetSocketAddress ( SocketAddress 的子类 )构造方法
在这里插入图片描述
基于UDP实现回显服务器
回显服务器就是客户端发送什么请求服务器就返回什么请求,UDP是不需要建立连接的

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    private DatagramSocket socket;

    public UdpEchoServer(int port) throws SocketException {
        this.socket = new DatagramSocket(port);
    }
    private void start() throws IOException {
        System.out.println("服务器启动成功");
        while (true) {
            // 1.读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            this.socket.receive(requestPacket);
            String request = new String(requestPacket.getData());
            // 2.根据请求计算响应
            String response = process(request);
            // 3.把响应返回给客户端
            DatagramPacket responsePacket = new DatagramPacket(request.getBytes(),0,request.getBytes().length,
                    requestPacket.getSocketAddress());
            this.socket.send(responsePacket);
            // 4.打印日志
            String log = String.format("[%s:%d] request: %s  response: %s",requestPacket.getAddress().toString(),requestPacket.getPort(),
                    request,response);
            System.out.println(log);
        }
    }

    /**
     * 这是一个回显服务器
     * @param request
     */
    private String process(String request) {
        //发送什么请求就返回什么响应
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}


客户端代码

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;
    private int serverPort;
    private String serverIp;
    private InetSocketAddress inetSocketAddress;
    public UdpEchoClient(int port,String ip) throws SocketException {
    //客户端的IP和端口号由操作系统自动分配
        this.socket = new DatagramSocket();
        this.serverPort = port;
        this.serverIp = ip;
        this.inetSocketAddress = new InetSocketAddress(this.serverIp, this.serverPort);
    }
    public void start() throws IOException {
        System.out.println("客户端启动成功");
        Scanner sc = new Scanner(System.in);
        while (true) {
            // 1.从键盘输入请求并构造
            System.out.print("-> ");
            String request = sc.nextLine();
            if ("exit".equals(request)) {
                String log = String.format("客户端退出[%s:%d]",this.serverIp,this.socket.getPort());
                System.out.println(log);
                break;
            }
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,this.inetSocketAddress);
            // 2.把请求发送给服务器
            this.socket.send(requestPacket);
            // 3.从服务器获取响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            this.socket.receive(responsePacket);
            String response = new String(responsePacket.getData());
            // 4.打印日志
            String log = String.format("[%s:%d] request: %s    response: %s",this.serverIp,responsePacket.getPort(),request,response);
            System.out.println(log);
        }
    }

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

在这里插入图片描述

TCP套接字
TCP的套接字API和UDP是完全 不同的

ServerSocket API
ServerSocket 是创建TCP服务端Soket的API

ServerSocket构造方法
在这里插入图片描述
ServerSocket方法
在这里插入图片描述
Socket API
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,放回的服务端Socket
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来和对方收发数据的

Socket 构造方法
在这里插入图片描述
Socket方法
在这里插入图片描述
TCP中的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接

短连接: 每次收到数据并返回响应后,都关闭

长连接: 不关闭连接,一直保持连接状态,双方不停的收发数据,就是长连接,也就是说,长连接可以多次收发数据

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

建立连接、关闭连接的耗时

短连接每次请求、响应都需要建立连接,关闭连接。而长连接至需要第一次连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是耗时的,长连接效率更高

主动发送请求不同

短连接一般是客户端主动向服务器发送请求,而长连接可以是客户端主动发送请求,也可以是服务端主动发

两者的使用场景不同

短连接适用于客户端请求频率不高的场景,入浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室、实时游戏等

基于TCP实现回显服务器
服务器代码

创建ServerSocket 对象指定端口号
TCP套接字双方要先建立连接
使用Thread处理多个客户端的情况
每次一个Socket使用完后一定要关闭

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoServer {
    private ServerSocket listenSocket;
    public TcpEchoServer(int port) throws IOException {
        this.listenSocket = new ServerSocket(port);
    }
    private void start() throws IOException {
        System.out.println("服务器启动成功");

        while (true) {
            // TCP套接字先要建立连接
            Socket socket = this.listenSocket.accept();
            //用Thread来处理多个客户端的情况
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        connectionProcess(socket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        }
    }
    private void connectionProcess(Socket socket) throws IOException {
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) {
            Scanner sc = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            String log = String.format("[%s:%d] 客户端上线",socket.getInetAddress(),socket.getPort());
            System.out.println(log);
            while (true) {
                // 1.读取请求并解析
                if (!sc.hasNext()) {
                    log = String.format("[%s:%d] 客户端下线",socket.getInetAddress(),socket.getPort());
                    System.out.println(log);
                    break;
                }
                String request = sc.nextLine();
                // 2.根据请求计算响应
                String response = process(request);
                // 3.把响应发给客户端
                printWriter.println(response);
                //加上flush刷新缓冲区
                printWriter.flush();
                // 4.打印日志
                log = String.format("[%s:%d] request: %s  response: %s",socket.getInetAddress(),socket.getPort(),
                        request,response);
                System.out.println(log);
            }
            sc.close();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //使用后关闭,防止内存泄露
            socket.close();
        }
    }

    /**
     * 回显服务器直接返回请求
     * @param request
     * @return
     */
    private String process(String request) {
        return request;
    }

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



import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    private Socket clientSocket;
    private String serverIp;
    private int serverPort;

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

    public void start() {
        System.out.println("客户端启动成功");
        Scanner sc = new Scanner(System.in);
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            Scanner responseSc = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                // 1.从键盘输入请求
                System.out.print("-> ");
                String request = sc.nextLine();
                if ("exit".equals(request)) {
                    break;
                }
                // 2.发送请求给服务器
                printWriter.println(request);
                //加上flush刷新缓冲区
                printWriter.flush();
                // 3.从服务器获取响应
                String response = responseSc.nextLine();
                // 4.打印日志
                String log = String.format("[%s:%d] request: %s  response: %s",this.serverIp,this.serverPort
                ,request,response);
                System.out.println(log);
            }
            responseSc.close();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/764140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows NT 驱动程序的编译、安装、调试

Windows NT 驱动程序的编译、安装、调试 Windows NT 驱动介绍NT 驱动代码分析编译安装修改注册表进行安装使用工具 DriverMonitor 进行打开加载安装 调试 Windows NT 驱动介绍 Windows 驱动分为两类,一类是从 Windows NT 遗留下来的驱动模型称为传统的 Windows NT …

1、Winform项目创建

项目创建的过程时比较简单的,要点在于选择基本库。 这里我们选择.Netframework 4.7.2,为什么使用这个呢?因为这个大多数windows系统上都装的有。如果使用.Net Core什么的,可能还需要再下载对应的运行库,影响用户体验。 具体步骤如下: 选择创建新项目 选择创建桌面应用…

RabbitMQ如何保证消息的可靠性6000字详解

RabbitMQ通过生产者、消费者以及MQ Broker达到了解耦的特点,实现了异步通讯等一些优点,但是在消息的传递中引入了MQ Broker必然会带来一些其他问题,比如如何保证消息在传输过程中可靠性(即不让数据丢失,发送一次消息就…

GDB调试基础知识

文章目录 概念准备工作常用命令说明启动与退出给程序设置参数/获取设置参数GDB使用帮助查看当前文件代码查看非当前文件代码查看及设置显示的行数断点操作调试操作 概念 GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,…

Python基础教程——60个基础练习(三)

41-字符串格式化 "%s is %s years old" % (bob, 23) # 常用 "%s is %d years old" % (bob, 23) # 常用 "%s is %d years old" % (bob, 23.5) # %d 是整数 常用 "%s is %f years old" % (bob, 23.5) "%s is %5.2f…

ListBox基本用法

作用:列表框,用于以列表的形式展示数据。 常用属性: 允许多列显示数据 添加数据项集合 常用事件: 选择项变化时触发该事件 后台代码示范: //列表框项目选择变化时被触发private void listBox1_SelectedIndexChanged…

Flutter 跳转应用市场评分——超简洁实现

最近在做flutter跳转去应用市场评分的功能,虽然是一个很小的功能,但是要做的既简单又高效,同时又能把细节考虑到,还是有坑要走的,这边记录一下。 背景 做应用市场相关的运营,在app内增加评分引导&#xf…

经典目标检测R-CNN系列(1)开山之作R-CNN

经典目标检测R-CNN系列(1)开山之作R-CNN 2014年,大神RBG(Ross Girshick)等人将卷积神经网络(Convolutional Neural Network,CNN)应用于目标检测任务中,在PASCAL VOC 2012数据集上,能…

vue 如何发布并部署到服务器

一般情况npm run build即可 从而生成vue代码直接放到服务器即可 这里的具体情况要看package.json里面的配置从而使用命令 会生成dist就是该项目的发布包

软件测试项目经验重要吗?

目前从行业薪资排名看,IT行业是我们普通人能够接触到的高薪行业,像金融、银行和投行等高薪职位,张雪峰老师在他的视频中分析过,不是一般人可以拿捏的。IT行业的大部分岗位需要专业的技能,留给我们这些非计算机专业科班…

实现微信机器人开发,个微api

首先微信聊天机器人,是一种通过自然语言模拟人类进行对话的程序。通常运行在特定的软件平台上,如PC平台或者移动终端设备平台。 有兴趣的可以去进行测试(E云管家),功能十分全面 文档测试过程中实现多项功能进行管理 …

数据结构--线性表的链式存储结构

这里写目录标题 链式存储结构链表简介格式分类头结点位置示意图与不带头结点的区别 链表的特点 单链表定义链表的代码实现简介实操 基本操作的实现初始化单链表销毁单链表清空单链表求单链表表长 二级目录二级目录二级目录二级目录二级目录二级目录 链式存储结构 链表 简介 格…

QML学习day1

QML学习day1 main.qml import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.5Window {width: 640height: 480visible: truecolor:"blue"title: qsTr("Hello World")Button {//按钮id: btn1width: 50height: 50focus: true //聚焦…

P106-100组A卡(R5 240)指南

P106-100组A卡(R5 240)指南 不建议小白尝试 不建议小白尝试 不建议小白尝试文章目录 P106-100组A卡(R5 240)指南资料合集硬件软件基础卸载所有原驱动安装驱动修改注册表自动调用——只改一个注册表手动调用——改两个注册表 劝退…

软件设计原则

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。 开闭原则 对扩展开放,对修改关闭。在程序…

Leecode402:移掉 K 位数字

这道题一看想的是可能用回溯或者什么别的方法,但是那样的话时间复杂度非常高,而且也不适用于动态规划,所以观察的话,可以知道从前往后判断的话肯定是前面越小越好,所以只需要前面最小,整体就最小。因此从前…

子网掩码详解

1 子网掩码 IP地址是以网络号和主机号来标示网络上的主机的,我们把网络号相同的主机称之为本地网络,网络号不相同的主机称之为远程网络主机,本地网络中的主机可以直接相互通信;远程网络中的主机要相互通信必须通过本地网关&#…

酸蚀刻对钛医药材料纳米形态表面特性及活化能的影响

引言 由于商业纯钛(CP Ti)具有抗腐蚀性,并且具有哦合适的机械性能以及生物相容性,因此,目前一直被用作牙科植入材料。为了在临床手术中获得高水平的成功,CP Ti的表面质量和形貌是影响植入手术结果的最关键因素之一,近…

GPT使用技巧

五大原则 想要让ChatGPT产出有效的回答,需要遵循以下五个原则: 提问清晰: 请尽可能清晰地描述您的问题 简明扼要: 请尽量使用简单的语言和简洁的句子来表达您的问题 确认问题: 请确认您的问题是清晰、明确和完整…

python接口自动化--token登录(详解)

简介 为了验证用户登录情况以及减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。有些登录不是用 cookie 来验证的,是用 token 参数来判断是否登录。token 传参有两种一种是放在请求头里,本质上是跟 cookie 是一样的&…