JavaEE: 创造无限连接——网络编程中的套接字

文章目录

  • Socket套接字
    • TCP和UDP的区别
      • 有连接/无连接
      • 可靠传输/不可靠传输
      • 面向字节流/面向数据报
      • 全双工/半双工
    • UDP/TCP api的使用
      • UDP
        • DatagramSocket
        • DatagramPacket
        • InetSocketAddress
        • 练习
      • TCP
        • ServerSocket
        • Socket
        • 练习


Socket套接字

Socket是计算机网络中的一种通信机制,通过在应用程序之间建立网络连接,实现数据的传输和交流。它提供了一组接口和协议,使得网络上的不同设备或进程能够进行通信。

换句话说,就是操作系统给应用程序(传输层给应用层)提供的API起了个名字,就叫做Socket.

我们通过代码来直接操作网卡,不太好操作.
因为网卡有很多种型号,之间提供的api都会有差别.
为了方便程序员操作网卡,操作系统就把网卡这个概念给封装成Socket,应用程序员就不必关注硬件的差异和细节,只需要操作Socket对象就能间接的操作网卡.
就跟遥控器差不多~

需要注意的是,接下来要说的都是操作系统提供的Java版本的Socket api.不是系统原生的api,而是JDK封装好的!

Socket api提供了两组不同的api

  • UDP有一套
  • TCP也有一套

TCP和UDP的区别

TCP: 有连接,可靠传输,面向字节流,全双工.
UDP: 无连接,不可靠传输,面向数据包,全双工.

有连接/无连接

此处谈到的连接,是"抽象"的连接.
通信双方,如果保存了通信对端的信息,就相当于是"有连接",如果不保存对端的信息,就是"无连接".

举个例子,比方说结婚.

结婚证,一式两份,本子上写着新郎和新娘两个人的名字/照片等信息.

一份由新郎保存.
一份由新娘保存.

此时结婚的这两个人就相当于建立了"抽象的/逻辑上的"连接.

可靠传输/不可靠传输

此处谈到的"可靠",不是指100%能到达对方,而是"尽可能".
因为网络环境非常复杂,存在很多不确定的因素~

再厉害的技术,也顶不过挖掘机一铲子~

相对来说"不可靠",就是指完全不考虑数据是否能到达对方.

TCP内置了一些机制,能够保证可靠传输

  1. 感知到对方是不是收到了
  2. 重传机制,在对方没收到的时候进行重试

UDP则没有可靠性机制,完全不管发出去的数据是否能够到达对方.

面向字节流/面向数据报

TCP是面向字节流的,TCP的传输过程就和文件流/水流是一样的特点.
在这里插入图片描述
UDP是面向数据报的.
此时,传输数据的基本单位就不是字节了,而是"UDP数据报".
一次发送/接收,必须发送/接收完整的UDP数据报.

全双工/半双工

全双工: 一个通信链路,可以发送数据,也可以接收数据.(双向通信)

半双工: 一个通信链路,只能发送/接收(单向通信).

UDP/TCP api的使用

UDP

DatagramSocket

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

DatagramSocket构造方法:
在这里插入图片描述

DatagramSocket方法:
在这里插入图片描述

DatagramPacket

DatagramPacket是UDP Socket发送和接收的数据报.

DatagramPacket构造方法:
在这里插入图片描述

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

InetSocketAddress

InetSocketAddress (SocketAddress的子类)构造方法:
在这里插入图片描述

练习

写一个最简单的客户端服务器程序,“回显服务器”(echo server).客户端发啥样的请求,服务器就返回啥样的响应.

服务器代码

package javaEE.Internet;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class A {private DatagramSocket socket = null;// SocketException 这个异常是IOException的子类public A(int port) throws SocketException {// 对于服务器这一端来说,需要在Socket对象创建的时候,就指定一个端口号,作为构造方法的参数// 后续服务器开始运行之后,操作系统就会把端口号和该进程关联起来.// 创建Socket一定要指定端口号,服务器必须是指定了端口号,客户端主动发起请求的时候,才能找到服务器socket = new DatagramSocket(port);// 在调用这个构造方法的过程中,jvm就会调用系统的Socket api来完成"端口号-进程"之间的关联动作// 对于一个系统来说,同一时刻,一个端口号,只能被一个进程绑定// 但是一个进程可以绑定多个端口号(通过多个Socket对象来完成)}// 通过start来启动服务器的核心流程public void start() throws IOException {System.out.println("服务器启动!");while (true) {// 通过死循环来不停的处理客户端的请求// 1. 读取客户端的请求并解析// DatagramPacket自身需要存储数据,但是存储数据的空间具体多大,需要外部来定义DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);//	receive是从网卡上读取的数据,但是调用receive的时候,网卡上可不一定就有数据.// 如果网卡上收到数据了,receive立即返回获取到的数据,如果网卡上没有收到数据,receive就会阻塞等待,一直等待到真正收到数据为止.socket.receive(requestPacket);// 上述收到的数据是二进制byte[]的形式体现的,后续代码如果要进行打印之类的处理操作// 那就需要转换成字符串才好处理String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求计算响应,由于此处是回显服务器,响应就是请求.String response = process(request);// 3. 把响应写回到客户端// response.getBytes().length 这里容易写错,容易写成response.length().// response.getBytes().length获取字节数组,得到字节数组的长度,单位是"字节".// response.length()获取字符串中"字符"的个数,单位是"字符".DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());// UDP有一个特点,无连接.// 所谓的连接,就是通信双方,保存对方的信息(IP+端口)// DatagramPacket 这个对象中,不持有对方(客户端)的ip和端口.// 在进行send的时候,就需要在send的数据包里,把要发给谁这样的信息写进去,才能够正确的把数据返回.socket.send(responsePacket);// 4. 把日志打印一下System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress(), requestPacket.getPort(),request, response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {A a = new A(9090);a.start();}
}

客户端代码

package javaEE.Internet;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class B {private DatagramSocket socket = null;private String serverIP;private int severPort;public B(String serverIP, int severPort) throws SocketException {// 客户端这边,创建Socket,最好不要指定端口号// 客户端是主动的一方,不需要让服务器来找他// 客户端就不需要指定端口号了(不指定不代表没有,客户端这边的端口号是系统自动分配了一个端口)// 这里还有一个重要的原因,如果客户端这里指定了端口号之后,由于客户端是在用户的电脑上运行的,天知道用户的电脑上都有哪些程序,都已经占用了哪些端口号了socket = new DatagramSocket();this.serverIP = serverIP;this.severPort = severPort;}public void start() throws IOException {System.out.println("启动客户端!");Scanner scanner = new Scanner(System.in);while (true) {// 1. 从控制台读取到用户的输入System.out.print("-> ");String request = scanner.next();// 2. 构造出一个UDP请求,发送给服务器// 此处是给服务器发送数据,发送数据的时候UDP数据报里就需要带有目标的IP和端口// 接收数据的时候,构造出的UDP数据报,就是一个空的数据报.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(this.serverIP),this.severPort);socket.send(requestPacket);// 3. 从服务器读取到响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0,responsePacket.getLength());// 4. 把响应打印到控制台上System.out.println(response);}}public static void main(String[] args) throws IOException {B b = new B("127.0.0.1",9090);// "127.0.0.1"这个是特殊的IP,环回IP// 这个IP就代表本机,如果客户端和服务器在同一个主机上,就使用这个IPb.start();}
}

运行结果:
在这里插入图片描述

在以上代码的基础上,实现一个"英译汉"的效果.

"英译汉"服务器代码:

package javaEE.Internet;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;// 实现一个"英译汉"
public class C extends A{private Map<String,String> dict = new HashMap<>();@Overridepublic String process(String request) {return dict.getOrDefault(request,"未知单词");}public C(int port) throws SocketException {super(port);dict.put("cat","小猫");dict.put("dog","小狗");dict.put("pig","小猪");dict.put("sheep","小羊");// 真实的服务器,需要很多的单词,可能是上万个.}public static void main(String[] args) throws IOException {C server = new C(9090);server.start();}
}

客户端代码不需要改动~
运行效果:
在这里插入图片描述

TCP

ServerSocket

ServerSocket是创建TCP服务端Socket的API.

ServerSocket构造方法:

在这里插入图片描述

同一个协议下,一个端口号只能被一个进程绑定.
比如,9090端口,在UDP下被一个进程绑定了;9090这个端口,还可以在TCP下被另一个进程绑定.

ServerSocket方法:
在这里插入图片描述

Socket

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

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

Socket构造方法:
在这里插入图片描述

Socket方法:
在这里插入图片描述
InputStream和OutputStream称为"字节流".
前面针对文件操作的方法,针对此处的tcp socket来说,也是完全适用的.

这里为啥没有类,来表示一个"TCP数据报"呢?
TCP是面向字节流的,TCP上传输数据的基本单位就是byte.
UDP是面向数据报,UDP这里需要定义专门的类,来表示UDP数据报,作为UDP传输的基本单位

练习

编写TCP回显服务器.

服务器代码:

package javaEE.Internet;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 D {private ServerSocket serverSocket = null;public D(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("启动服务器");while (true) {// 像ServerSocket,DatagramSocket,它们的生命周期,都是跟随整个进程的// 这里的clientSocket是"连接级别"的数据// 随着客户端断开连接,这个socket也就不再使用了.(即使是同一个客户端,断开之后,重新连接,也是一个新socket,和旧socket不是同一个了)// 因此,这样的socket就应该主动关闭掉,避免出现文件资源泄漏的问题.Socket clientSocket = serverSocket.accept();Thread t = new Thread(() -> {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}}// 针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) throws IOException {// 先打印一下客户端的信息System.out.printf("[%s,%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 获取到socket中持有的流对象.// TCP是全双工通信的通信,一个socket对象,既可以读,也可以写~try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 使用Scanner包装一下inputStream,就可以更方便的读取这里的请求数据了Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {// 1. 读取请求并解析if (!scanner.hasNext()) {// 如果Scanner无法读取出数据,说明客户端关闭了连接,导致服务器这边读取到"末尾"break;}String request = scanner.next();// 2. 根据请求计算相应String response = process(request);// 3. 把响应写回给客户端// 此处可以按照字节数组来写,也可以有另一种写法// outputStream.write(response.getBytes());// printWriter.println(response);可以让我们在写入的时候加上 \n // 由于上面if语句里写了!scanner.hasNext(),这就意味着请求应该是以"空白符"(空格,回车,制表符,垂直制表符,翻页符...)结尾的.// 因此此处就约定,使用\n来作为请求和响应的结尾标志.// 后续客户端,也会使用scanner.next来读取响应.printWriter.println(response);// 由于printWriter这样的类,以及很多IO流中的类,都是"自带缓冲区"的// 引入缓冲区之后,进行写入操作,不会立即触发IO,而是先放到内存缓冲区中,等缓冲区里攒了一些后,再统一进行发送// 由于此处的数据比较少,因此这样的数据就会一直停留在缓冲区中,出不去了~// 为了让较少的数据也能发送出去,这里就可以引入flush操作,来主动"刷新缓冲区".printWriter.flush();// 4. 打印日志System.out.printf("[%s,%d] req=%s; resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(), request, response);}} catch (IOException e) {e.printStackTrace();} finally {System.out.printf("[%s,%d]客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());clientSocket.close();}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {D server = new D(9090);server.start();}
}

客户端代码:

package javaEE.Internet;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class E {private Socket socket = null;public E(String serverIp, int severPort) throws IOException {socket = new Socket(serverIp, severPort);}public void start() {System.out.println("客户端启动!");try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {Scanner scanner = new Scanner(inputStream);Scanner scannerIn = new Scanner(System.in);PrintWriter printWriter = new PrintWriter(outputStream);while ((true)) {// 1. 从控制台读取数据System.out.print("->");String request = scannerIn.next();// 2. 把请求发送给服务器printWriter.println(request);printWriter.flush();// 3. 从服务器读取相应if (!scanner.hasNext()) {break;}String response = scanner.next();// 4. 打印相应结果System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {E client = new E("127.0.0.1", 9090);client.start();}
}

运行效果:
在这里插入图片描述

本文到这里就结束啦~

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

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

相关文章

【VLM小白指北 (1) 】An Introduction to Vision-Language Modeling

开一个新坑Vision-Language Modeling (VLM) &#xff0c;原文76页&#xff0c;慢慢更&#xff0c;for beginners&#xff0c;但也不能之前啥都不会啊。 原文链接&#xff1a;An Introduction to Vision-Language Modeling Introduction 存在的问题&#xff1a;将语言与视觉相…

ChatGPT 在国内使用的方法

AI如今很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;ChatGPT 真是无所不能~ 作为一款出色的大语言模型&#xff0c;ChatGPT 实现了人类般的对话交流&#xff0c;最主要是能根据上下文进行互动。 接下来&#xff0c;我将介绍 ChatGPT 在国…

xhs 小红书 x-s web 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 有相关问题请第一时间头像私信联系我…

《深度学习》PyTorch框架 优化器、激活函数讲解

目录 一、深度学习核心框架的选择 1、TensorFlow 1&#xff09;概念 2&#xff09;优缺点 2、PyTorch 1&#xff09;概念 2&#xff09;优缺点 3、Keras 1&#xff09;概念 2&#xff09;优缺点 4、Caffe 1&#xff09;概念 2&#xff09;优缺点 二、pytorch安装 1、安装 2、…

Linux操作系统:GCC(GNU Compiler Collection)编译器

在 Linux 系统中&#xff0c;gcc&#xff08;GNU Compiler Collection&#xff09;是一个非常强大的编译器&#xff0c;主要用于编译 C 语言程序。 除了基本的编译和链接命令外&#xff0c;gcc还提供了许多选项和功能。 以下是一些常用的 gcc命令及其功能&#xff1a; 1. 基本…

Python | Leetcode Python题解之第420题强密码检验器

题目&#xff1a; 题解&#xff1a; class Solution:def strongPasswordChecker(self, password: str) -> int:n len(password)has_lower has_upper has_digit Falsefor ch in password:if ch.islower():has_lower Trueelif ch.isupper():has_upper Trueelif ch.isdi…

基于SpringBoot+Vue的智慧物业管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目源码、Python精…

transformer模型进行英译汉,汉译英

上面是在测试集上的表现 下面是在训练集上的表现 上面是在训练集上的评估效果 这是在测试集上的评估效果,模型是transformer模型,模型应该没问题,以上的是一个源序列没加结束符和加了结束符的情况。 transformer源序列做遮挡填充的自注意力,这就让编码器的输出中每个token的语…

寄存器与内存

第三课&#xff1a;寄存器与内存、中央处理器&#xff08;CPU&#xff09;、指令和程序及高级 CPU 设计-CSDN博客 锁存器 引入 ABO0&#xff08;开始状态&#xff09;001&#xff08;将A置1&#xff09;110&#xff08;将A置0&#xff09;11 无论怎么做&#xff0c;都没法从1变…

大学生必看!60万人在用的GPT4o大学数学智能体有多牛

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1…

Mamba所需的causal-conv1d 和mamba-ssm库在哪下载?

背景介绍 参照 Mamba [state-spaces/mamba: Mamba SSM architecture (github.com)] github中提到的环境安装[Installation 一栏] [Option] pip install causal-conv1d>1.4.0: an efficient implementation of a simple causal Conv1d layer used inside the Mamba block.…

Qt_窗口界面QMainWindow的介绍

目录 1、菜单栏QMenuBar 1.1 使用QMainWindow的准备工作 1.2 在ui文件中设计窗口 1.3 在代码中设计窗口 1.4 实现点击菜单项的反馈 1.5 菜单中设置快捷键 1.6 菜单中添加子菜单 1.7 菜单项中添加分割线和图标 1.8 关于菜单栏创建方式的讨论 2、工具栏QToolBar …

k8s Service 服务

文章目录 一、为什么需要 Service二、Kubernetes 中的服务发现与负载均衡 -- Service三、用例解读1、Service 语法2、创建和查看 Service 四、Headless Service五、集群内访问 Service六、向集群外暴露 Service七、操作示例1、获取集群状态信息2、创建 Service、Deployment3、创…

飞腾计算模块RapidIO性能测试

1、背景介绍 飞腾计算模块采用FT2000 64核处理器&#xff0c;搭配Tsi721 PCIE转RapidIO芯片&#xff0c;实现飞腾平台下的SRIO数据通信。操作系统采用麒麟信安&#xff0c;内核版本4.19.90. 2、驱动加载 驱动加载部分类似之前写过的X86平台下的RapidIO驱动加载&#xff0c;具…

Rsync未授权访问漏洞复现及彻底修复

一、什么是 Rsync&#xff1f; Rsync 是一种广泛使用的文件传输工具&#xff0c;它允许系统管理员和用户通过局域网&#xff08;LAN&#xff09;或广域网&#xff08;WAN&#xff09;在计算机之间同步文件和目录。Rsync 支持通过本地或远程 shell 访问&#xff0c;也可以作为守…

【Linux】常用指令详解一(ls,-a,-l,-d,cd,pwd,mkdir,touch,rm,clear)

1.前言 读了一些Linux常用指令的博文&#xff0c;很可惜没读到一点点手把手教怎么操作的博文&#xff0c;所以写一篇手把手教适合初学者的Linux常用指令博文 Linux的命令是树状结构 输入这一句命令&#xff1a;yum install -y tree 即可以查看Linux树状目录结构 查看示例&am…

STM32快速复习(十二)FLASH闪存的读写

文章目录 一、FLASH是什么&#xff1f;FLASH的结构&#xff1f;二、使用步骤1.标准库函数2.示例函数 总结 一、FLASH是什么&#xff1f;FLASH的结构&#xff1f; 1、FLASH简介 &#xff08;1&#xff09;STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&…

pytorch实现RNN网络

目录 1.导包 2. 加载本地文本数据 3.构建循环神经网络层 4.初始化隐藏状态state 5.创建随机的数据&#xff0c;检测一下代码是否能正常运行 6. 构建一个完整的循环神经网络 7.模型训练 8.个人知识点理解 1.导包 import torch from torch import nn from torch.nn imp…

Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装

音频解析 音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。 FFmpeg音频解码的基本步骤如下: 初始化FFmpeg解码器(4.0版本后可省略): 调用av_register_all()初始化编解码器。 调用avcodec_register_all()注册所有编解码器。 打开输入的音频流:…

pthread_cond_signal 和pthread_cond_wait

0、pthread_join()函数作用&#xff1a; pthread_join() 函数会一直阻塞调用它的线程&#xff0c;直至目标线程执行结束&#xff08;接收到目标线程的返回值&#xff09;&#xff0c;阻塞状态才会解除。如果 pthread_join() 函数成功等到了目标线程执行结束&#xff08;成功获取…