网络编程——套接字

目录

一、Socket套接字

(一)概念

(二)分类

1.流套接字:

2.数据报套接字

3.原始套接字

二、TCP协议VSUDP协议

(一)有连接VS无连接

(二)可靠传输VS不可靠传输

(三)面向字节流VS面向数据报

(四)全双工VS半双工

三、UDP协议中的socket api

(一)DatagramSocket类

(二)DatagramPacket类

(三)InetSocketAddress类

四、UDP协议的回显服务器

(一)UdpEchoServer回显服务器

(二)UdpEchoClient客户端 

(三)拓展:英译汉服务器 

五、TCP协议中的socket api

(一)ServerSocket类

(二)Socket类

六、TCP协议的回显服务器

(一)TcpEchoServer回显服务器

(二)TcpEchoClient客户端


一、Socket套接字

(一)概念

Socket套接字,是有系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

(二)分类

1.流套接字:

使用传输层TCP协议,是以字节流的格式来通信的。对于字节流来说,可以简单的理解为传输数据是基于IO流,流失数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次发送。

2.数据报套接字

使用传输层UDP协议,是以数据报的格式来通信的。对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

3.原始套接字

原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

二、TCP协议VSUDP协议

TCP的特点:

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

UDP的特点:

  • 无连接
  • 不可靠传输
  • 面向数据报
  • 全双工

(一)有连接VS无连接

这是抽象的概念,指的是虚拟的/逻辑上的连接。

  • 对于TCP来说,TCP协议中,就保存了对端的信息: A和B通信,A和B先建立连接,让A保存B的信息,B保存A的信息(彼此之间知道要连接的是哪个)。
  • 对于UDP来说,UDP协议本身,不保存对方的信息,就是无连接。

(二)可靠传输VS不可靠传输

在网络上,数据是非常容易出现丢失的情况的(丢包),光信号/电信号都可能受到外界的干扰。

在进行通信时,不能指望一个数据包100%地到达对方。

  • 可靠传输指的是,虽然不能保证数据包100%到达,但是能尽可能提高传输成功的概率。
  • 不可靠传输只是把数据发了,就不管了。

(三)面向字节流VS面向数据报

  • 面向字节流指的是在读写数据时,以字节为单位。
  • 面向数据报指的是读写数据时,以数据报为单位。

(四)全双工VS半双工

  • 全双工指的是 一个通信链路中,支持双向通信(能读也能写)。
  • 半双工指的是 一个通信链路中,只支持单向通信(要么读,要么写)。

三、UDP协议中的socket api

计算机中的“文件”,是一个广义的概念,文件还能代指一些硬件设备(操作系统管理硬件设备,也是抽象成文件,统一管理的)。

UDP协议是用来操作网卡的,将网卡抽象成socket文件,操作网卡的时候,流程和操作普通文件差不多。

(一)DatagramSocket类

DatagramSocket类是用来操作socket文件,发送和接收数据报的。

构造方法:

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

成员方法: 

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

(二)DatagramPacket类

DatagramPacket就是UDP发送和接收的数据报。

构造方法:

方法签名方法说明
DatagramPacket(byte[]buf,int length)构造一个DatagramPacket用来接收数据报,接收的数据报保存在字节数组中(第一个参数buf),接收的指定长度(第二个参数length)。
DatagramPacket(byte[]buf,int offset,int length,SocketAddress address)构造一个DatagramPacket用来接收数据报,接收的数据报保存在字节数组中(第一个参数buf),指定起点(第二个参数offset),接收的指定长度(第三个参数length)。address指定目的主机的IP和端口号。

成员方法:

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

(三)InetSocketAddress类

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

InetSocketAddress的构造方法:

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

四、UDP协议的回显服务器

Java数据报套接字通信模型:

(一)UdpEchoServer回显服务器

package NetWork;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;//UDP协议的回显服务器
//服务器端
public class UdpEchoServer {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);//2.根据请求, 计算响应. (服务器最关键的逻辑)//把读取到的二进制数据, 转成字符串. 只是构造有效的部分.String request=new String(RequestPacket.getData(),0, RequestPacket.getLength());String response=process(request);//3.把响应返回给客户端//根据 response 构造 DatagramPacket, 发送给客户端.//此处不能使用 response.length(),因为这是String的长度而不是byte数组的长度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客户端 

package NetWork;import java.io.IOException;
import java.net.*;
import java.util.Scanner;//UDP协议的回显服务器
//客户端
public class UdpEchoClient {DatagramSocket socket=null;// 客户端要给服务器发送数据报,首先得知道服务器的IP和端口号private String ServerIp;//目的IPprivate int ServerPort;//目的端口号// 和服务器不同, 此处的构造方法是要指定访问的服务器的地址.public UdpEchoClient(String serverIp, int serverPort) throws SocketException {this.ServerIp = serverIp;this.ServerPort = serverPort;socket = new DatagramSocket();}public void start() throws IOException {Scanner sc=new Scanner(System.in);while(true){// 1.读取用户输入的内容System.out.println("请输入要发送的内容:");if(!sc.hasNext()){break;}String request=sc.next();// 2. 把请求发送给服务器, 需要构造 DatagramPacket 对象.// 构造过程中, 不光要构造载荷, 还要设置服务器的 IP 和端口号DatagramPacket RequestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(ServerIp),ServerPort);// 3. 发送数据报socket.send(RequestPacket);// 4. 接收服务器的响应DatagramPacket ResponsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(ResponsePacket);// 5. 从服务器读取的数据进行解析, 打印出来.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);client.start();}
}

(三)拓展:英译汉服务器 

当我们需要实现另外一个简单的服务器时,例如英译汉服务器,只需要继承然后重写process方法就可以了。

package NetWork;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;//英译汉服务器
public class UdpDictServer extends UdpEchoServer{private HashMap<String,String> dict=new HashMap<>();//要在子类的构造方法中调用父类的构造方法//构造方法初始化字典public UdpDictServer(int port) throws SocketException {super(port);dict.put("apple","苹果");dict.put("boy","男孩");dict.put("cat","小猫");dict.put("dog","小狗");}//重写process方法public String process(String request){return dict.getOrDefault(request,"没有找到该词汇");}public static void main(String[] args) throws IOException {UdpDictServer DictServer=new UdpDictServer(9090);DictServer.start();}
}

五、TCP协议中的socket api

(一)ServerSocket类

ServerSocket是创建TCP服务器端Socket的API。

构造方法:

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

成员方法:

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

(二)Socket类

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

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

构造方法:

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

成员方法:

方法签名方法说明

InetAddress getInetAddress()

返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutPutStream()返回此套接字的输出流

六、TCP协议的回显服务器

Java流套接字通信模型:

(一)TcpEchoServer回显服务器

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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;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("启动服务器");// 这种情况一般不会使用 fixedThreadPool, 意味着同时处理的客户端连接数目就固定了.ExecutorService executorService = Executors.newCachedThreadPool();while (true) {// tcp 来说, 需要先处理客户端发来的连接.// 通过读写 clientSocket, 和客户端进行通信.// 如果没有客户端发起连接, 此时 accept 就会阻塞.// 主线程负责进行 accept, 每次 accept 到一个客户端, 就创建一个线程, 由新线程负责处理客户端的请求.Socket clientSocket = serverSocket.accept();// 使用多线程的方式来调整// Thread t = new Thread(() -> {// processConnection(clientSocket);// });// t.start();// 使用线程池来调整executorService.submit(() -> {processConnection(clientSocket);});}}private void processConnection(Socket clientSocket){//对clientSocket进行读写操作System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());try(InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream()){// 针对 InputStream 套了一层Scanner scanner = new Scanner(inputStream);// 针对 OutputStream 套了一层PrintWriter writer = new PrintWriter(outputStream);while(true){//因为输入流中的数据是持续读取的,要加上循环// 1. 读取请求并解析. 可以直接 read, 也可以借助 Scanner 来辅助完成.if (!scanner.hasNext()) {//scanner.hasNext():判断输入流中是否还有 “下一个令牌”(默认以空白字符分割,如空格、换行等)。// 连接断开了System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 2. 根据请求计算响应String request=scanner.next();String response=process(request);// 3. 返回响应到客户端// outputStream.write(response.getBytes());writer.println(response);//将缓存区中的数据都发送出去,避免残留writer.flush();// 打印日志System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request, response);}} catch (IOException e) {throw new RuntimeException(e);} finally {try {//服务器连接一个客户端就要创建一个clientSocket,使用完就要关闭.clientSocket.close();} catch (IOException e) {throw new RuntimeException(e);}}}private String process(String request){return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);tcpEchoServer.start();}
}

(二)TcpEchoClient客户端

package NetWork;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 TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {// 直接把字符串的 IP 地址, 设置进来.// 127.0.0.1 这种字符串socket = new Socket(serverIp, serverPort);}public void start()throws IOException{Scanner scanner=new Scanner(System.in);try(InputStream inputStream=socket.getInputStream();OutputStream outputStream=socket.getOutputStream()){//给inPutStream套一层Scanner scannerNet= new Scanner(inputStream);//给outPutStream套一层PrintWriter writer=new PrintWriter(outputStream);while (true){//1.读取用户输入String request=scanner.next();//2.发送请求并刷新缓存区数据writer.println(request);writer.flush();//3.接收服务器的响应String response=scannerNet.next();//4.打印出响应System.out.println(response);}}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);client.start();}
}

注意点:

  • 在服务器中,采用多线程的方式来处理客户端的请求(使用线程池)。因为如果是单线程有多个客户端连接,当程序处理processConnection请求时,就可能阻塞在processConnection,而不能accpet。
  • 因为服务器中有scanner.hasNext来判断发来的请求,所以客户端发送的请求要以换行符/空白符号结束,因此发送时用writer.println。
  • 发送请求后记得使用flush刷新缓冲区的数据。

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

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

相关文章

Git 基础操作笔记(速查)

1. 初始化仓库git init在当前文件夹初始化一个新的 Git 仓库。2. 克隆仓库git clone <仓库地址>从远程仓库复制项目到本地。3. 查看文件状态git status查看工作区和暂存区的文件状态。4. 添加文件到暂存区git add <文件名> git add . # 添加所有改动文件5. 提…

【并查集】P3367 【模板】并查集

P3367 【模板】并查集 题目背景 本题数据范围已经更新到 1≤N≤21051\le N\le 2\times 10^51≤N≤2105&#xff0c;1≤M≤1061\le M\le 10^61≤M≤106。 题目描述 如题&#xff0c;现在有一个并查集&#xff0c;你需要完成合并和查询操作。 输入格式 第一行包含两个整数 N,MN,M…

MyBatis流式查询详解

MyBatis 流式查询详解&#xff1a;ResultHandler 与 Cursor 在业务中&#xff0c;如果一次性查询出百万级数据并返回 List&#xff0c;很容易造成 OOM 或 长时间 GC。 MyBatis 提供了 流式查询&#xff08;Streaming Query&#xff09; 能力&#xff0c;让我们可以边读边处理&a…

1Panel Agent 证书绕过实现远程命令执行漏洞复现(CVE-2025-54424)

免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 前…

kettle插件-kettle http post plus插件,轻松解决https post接口无法调用文件流下载问题

场景&#xff1a;小伙伴在使用kettle调用https post接口过程中无法正常调用&#xff0c;程序出错问题&#xff0c;今天演示下用自研插件轻松解决这个问题。1、使用openssl 生成自签名证书openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 3652、…

剑指offer第2版——面试题2:实现单例

文章目录一、题目二、考察点三、答案3.1 C11写法3.2 C98写法&#xff08;线程安全只存在于懒汉模式&#xff09;3.2.1 小菜写法3.2.2 小菜进阶写法3.2.3 中登写法3.2.3 老鸟写法四、扩展知识4.1 饿汉模式和懒汉模式的区别4.1.1 饿汉模式&#xff08;Eager Initialization&#…

OpenAI开源大模型gpt-oss系列深度解析:从120B生产级到20B桌面级应用指南

引言&#xff1a;OpenAI开源里程碑&#xff0c;AI民主化加速到来 2025年8月&#xff0c;OpenAI正式宣布开源其两款重磅大语言模型——gpt-oss-120b&#xff08;1200亿参数生产级模型&#xff09;和gpt-oss-20b&#xff08;200亿参数桌面级模型&#xff09;&#xff0c;引发全球…

本地部署文档管理平台 BookStack 并实现外部访问( Windows 版本)

BookStack 是一款专注于书籍、文档管理的开源平台&#xff0c;它界面设计直观简洁&#xff0c;功能强大且易于使用&#xff0c;允许用户创建、组织和分享文档资料&#xff0c;特别适合用于构建内部文档系统、知识库或公开的文档站点。本文将详细介绍如何在 Windows 系统本地部署…

VS Code编辑器

实际上&#xff0c;‌Visual Studio Code&#xff08;简称VS Code&#xff09;‌是由微软开发的免费、开源、跨平台的代码编辑器&#xff0c;支持多种编程语言和框架&#xff0c;广泛应用于现代Web和云应用开发。这也是个编辑器&#xff0c;可能是继 GitHub 的 Atom 之后的一枝…

自动化测试篇--BUG篇

目录 一.软件测试的生命周期 二.bug是什么&#xff1f; 三.如何描述一个bug&#xff1f; 四.bug的级别 五.bug的生命周期 六.测试与开发产生争执怎么办&#xff1f;&#xff08;重要&#xff01;&#xff01;&#xff01;&#xff09; 一.软件测试的生命周期 软件测试人员…

Solidity智能合约基础

基础学习使用 remix&#xff1a;ide Remix - Ethereum IDE evm&#xff1a;ethreum virtual machine evm字节码 强类型脚本语言 compile >evm bytescode >evm hello的样例 声明的关键字&#xff1a;contract // SPDX-License-Identifier: MIT pragma solidi…

Unity跨平台超低延迟的RTSP/RTMP播放器技术解析与实战应用

✳️ 引言&#xff1a;为什么说 Unity 中的视频能力是“可视化神经元”&#xff1f; 随着“可视化 实时性”成为工业数字化的关键支撑&#xff0c;Unity 正从传统游戏引擎&#xff0c;演进为数字孪生系统、智能机器人中控、虚拟交互平台、XR 可视引擎等领域的底层核心。它不再…

python学智能算法(三十三)|SVM-构建软边界拉格朗日方程

【1】引用 在前序学习进程中&#xff0c;我们初步了解了SVM软边界&#xff0c;今天就更进一步&#xff0c;尝试构建SVM软边界的拉格朗日函数。 【2】基本问题 在SVM软边界中&#xff0c;我们已经获得此时的最优化几何距离的表达式&#xff1a; fmin⁡12∣∣w∣∣2C∑i1nξif…

【YOLOv5】

Focus模块&#xff1a;早期再yolov5版本提出&#xff0c;后期被常规卷积替换&#xff0c;作用是图像进入主干网络之前&#xff0c;进行隔行隔列采样&#xff0c;把空间维度堆叠到通道上&#xff0c;减少计算量。 SPPF:SPP的改进版本&#xff0c;把SPP的不同池化核改变为K 5 的…

Pytest项目_day05(requests加入headers)

headers 由于每个请求都需要加入一些固定的参数&#xff0c;例如&#xff1a;cookies、user-agent&#xff0c;那么将这些固定参数放入URL或params中会显得很臃肿&#xff0c;因此一般将这些参数放在request headers中headers的反爬作用 在豆瓣网站中&#xff0c;如果我们不加入…

安全引导功能及ATF的启动过程(四)

安全引导功能及ATF的启动过程&#xff08;四&#xff09; ATF中bl31的启动 在bl2中触发安全监控模式调用后会跳转到bl31中执行&#xff0c;bl31最主要的作用是建立EL3运行态的软件配置&#xff0c;在该阶段会完成各种类型的安全监控模式调用ID的注册和对应的ARM核状态的切换&am…

从手工到智能决策,ERP让制造外贸企业告别“数据孤岛“降本增效

在全球化竞争加剧的当下&#xff0c;制造型外贸企业正面临订单碎片化、供应链复杂化、合规风险上升等多重挑战。数字化转型已成为企业突破增长瓶颈、构建核心竞争力的必选项。然而&#xff0c;许多企业在推进过程中因选型不当陷入“系统孤岛”“数据失真”“流程低效”等困境。…

DMETL简单介绍、安装部署和入门尝试

一、DMETL的介绍1.1 概述我们先来简单了解一下DMETL。DMETL是什么&#xff1f;说的简单一点&#xff0c;DMETL一款数据处理与集成平台&#xff1b;从功能来说&#xff0c;那DMETL就是对数据同步、数据处理以及数据交换共享提供一站式支持的平台&#xff1b;从它的意义来说&…

NLP 人工智能 Seq2Seq、K-means应用实践

基于Java和人工智能的Web应用 以下是基于Java和人工智能的Web应用实例,涵盖自然语言处理、计算机视觉、数据分析等领域。这些案例结合了沈七星AI或其他开源框架(如TensorFlow、Deeplearning4j)的实现思路,供开发参考: 自然语言处理(NLP) 1. 智能客服系统 使用Java的Op…

Docker 从入门到实战(一):全面解析容器化革命 | 2025 终极指南

2025 年,全球容器市场规模突破 200 亿美元,超过 80% 的企业生产环境运行在容器之上。掌握 Docker 已成为开发、运维乃至架构师的核心竞争力。本文带你彻底搞懂 Docker 的底层逻辑与核心价值! 一、Docker 是什么?为什么它能改变世界? 想象一下:你开发时运行完美的 Pytho…