Socket编程

1. 什么是Socket

为了应用层和传输层能够交互,操作系统提供一些API给应用层,这些API可以把应用层的数据交给传输层,而这些API就是socket。
传输层中有很多协议,其中知名的就是TCP和UDP,因此操作系统提供了两个版本的API让应用层使用。

TCP和UDP的区别
  1. TCP是有连接,UDP无连接。(打电话是有连接的,要通信双方东建立好连接,才能通信;而发短信是无连接,不需要建立好连接)
  2. TCP是可靠传输,UDP是不可靠传输。(A给B发信息能够知道B有没有收到的是可靠传输)
  3. TCP是面向字节流,UDP是面向数据报。(一个以字节尾单位,一个以数据报尾单位)
  4. 都是全双工(类似于打电话交流,而半双工就类似于对讲机)
  5. 对于有可靠性要求的场景适用TCP,而对于可靠性要求不高,同时对传输效率要求很高的情况使用UDP

2. UDP的Socket编程

主要要掌握的两个类1. DatagramSocket 2. DatagramPacket

  1. DatagramSocket(网卡代言人)

DatagramSocket()


DatagramSocket(int port)


void receive(DatagramPacket p)


void send(DatagramPacket p)


void close()
创建UDP数据报套接字的Socket,绑定本机任意一个随机端口,一般用于客服端.


创建UDP数据报套接字的Socket,绑定本机指定端口,一般用于服务端


从此套接字接收数据报,如果没收到,就会阻塞等待


从此套接字发送数据报


关闭数据报套接字
socket本质上也是文件,socket对应网卡这个硬件设备,操作系统把网卡当作文件来管理,通过网卡发送数据,就是“写文件”,通过网卡接收数据,就是“读数据”

  1. DatagramPacket 数据报

DatagramPacket(byte[] buf, int length)


DatagramPacket(byte[]buf, int offset, int length,SocketAddress address)


InetAddress getAddress()


int getPort()


byte[] getData()
构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)


构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)。address指定目的主机的IP和端口


从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址


从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号


获取数据报中的数据

  1. 使用UDP实现回显服务器(复读机)
public class UDPEchoServer {
	private DatagramSocket socket = null;
	//服务器端要绑定端口,IP是本机,IP不用绑定
	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);//空的数据报
			//输出型参数,把请求写道requestPacket
			//阻塞等待直到收到请求
			socket.receive(requestPacket);
			//2.根据请求计算响应
			//把requestPacket转成字符串,方便打印
			String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
			String response = process(request);
			//3.把响应写回客服端,数据报要有客服端的地址
			//requestPacket.getSocketAddress() 客户端的地址IP,端口
			DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0, response.getBytes().length,
															   requestPacket.getSocketAddress());
			socket.send(responsePacket);
			//打印日志
			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(8080);
		server.start();
	}
}
public class UdpEchoClient {
	private DatagramSocket socket = null;
	//服务器的IP,端口先存起来,后面要用
	private String serverIp;
	private int serverPort;
	//客服端不需要手动指定端口,因为你不知道客服端哪些端口是开发的,哪些是被占用的
	//让操作系统自动分配一个空闲端口,客服端的IP是本机IP不用绑定
	public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
		socket = new DatagramSocket();
		//等会要转成是点分十进制,如127.0.0.1
		this.serverIp = serverIp;
		this.serverPort = serverPort;
	}
	public void start() throws IOException {
		Scanner scanner = new Scanner(System.in);
		while(true) {
			//用户输入内容
			System.out.println("->");
			String request = scanner.next();
			//构造一个UDP请求,包含内容,目的IP,目的端口
			//InetAddress.getByName(this.serverIp)把字符串IP转成点分十进制
			DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
															  InetAddress.getByName(this.serverIp), this.serverPort);
			socket.send(requestPacket);
			//3.从服务器读取UDP响应
			//空数据报
			DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
			//阻塞等待,直到收到响应
			socket.receive(responsePacket);//输出型参数,把响应写到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", 8080);
		client.start();
	}
}

流程图如下:
image.png

如何在idea中启动多个客户端

咱们能够实现多个客服端访问一个服务器
image.png

3.Tcp的Socket编程

  1. ServerSocket类(给服务端使用)

构造方法
ServerSocket(int port)
创建一个服务端流套接字Socket,绑定端口
普通方法
Socket accept()
接收客服端连接,返回一个服务端Socket,并阻塞等待
void close()
关闭socket

  1. Socket类(既给服务器用,又给客服端用)

构造方法
Socket(String host, int port)
创建一个客服端的Socket,并与对应的ip,端口的进程建立连接
普通方法
InetAddress getInetAddress()
返回对方的IP,端口
InputStream getInputStream()
OutputStream getOutStream()
返回此套接字的输入流
返回此套接字的输出流

  1. 实现TCP版本的回显服务器
public class TcpEchoServer {
	private ServerSocket listenSocket = null;
	//服务器端绑定端口
	public TcpEchoServer(int port) throws IOException {
		listenSocket = new ServerSocket(port);
	}
	public void start() throws IOException {
		System.out.println("服务器启动");
		//使用多线程,让多个服务端都能访问
		//使用线程池
		ExecutorService service = Executors.newCachedThreadPool();
		while(true) {
			//1. 使用accept()接受客服端的连接
			Socket clientSocket = listenSocket.accept();
			//2.处理客服端的连接,使用多线程,每个客服端连上来都分配一个新的线程负责处理
			service.submit(new Runnable() {
				@Override
				public void run() {
					try {
						processConnection(clientSocket);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
		}
	}

	private void processConnection(Socket clientSocket) throws IOException {
		System.out.printf("[%s:%d] 客服端上线\n", clientSocket.getInetAddress().toString(),
						  clientSocket.getPort());
		//处理客服端请求
		//放到try里面,结束时自动关闭输入,输出流
		try(InputStream inputStream = clientSocket.getInputStream();
			OutputStream outputStream = clientSocket.getOutputStream()) {
			while(true) {
				//1.读取请求并解析
				Scanner scanner = new Scanner(inputStream);
				if(!scanner.hasNext()) {
					//读完了,断开连接
					System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
									  clientSocket.getPort());
					break;
				}
				String request = scanner.next();
				//2.根据请求计算响应
				String response = process(request);
				//3.将响应写回客服端
				PrintWriter printWriter = new PrintWriter(outputStream);
				printWriter.println(response);
				//刷新缓冲区,保证数据一定通过网卡发送了
				printWriter.flush();
				System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
								  clientSocket.getPort(), request, response);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//一个客服端就创建一个clientSocket,要关闭防止文件资源泄露
			//而listenSocket是全局只有一个,不用关闭,随着进制结束而结束
			clientSocket.close();
		}
	}

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

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

如何理解listenSocket和clientSocket

listenSocket通过accept方法和客服端建立连接,一个clientSocket处理一个客服端连接。以卖房子的中介为例,listenSocket就类似外场销售,负责在外面拉客(可以拉很多客人),而clientSocket就是内场销售,一个内场销售服务一个客人。

为什么需要关闭clientSocket的连接

clientSocket在循环中,每个客服端连接上来就要分配一个,这个对象就会被反复的创建出实例,每创建一个,都要消耗一个文件描述符,,因此需要及时释放防止文件资源泄露

为什么需要多线程,而UDP版本的不需要

当没有客服端连接时,服务器就会阻塞到accept,如果第一个客服端过来,此时就会进入processConnection方法,连接建立好了以后,但是客服端还没发消息,此时代码阻塞在hasNext。当第二个客服过来,无法调用accept,也就无法处理第二个客服端,所以需要多线程。
UDP是无连接的,客服端直接请求即可,不必专注处理某个客服端。而TCP建立连接后,要处理客服端多次请求,才导致无法快速调用到accept方法,这是长连接。但改成短连接时TCP就能快速调用到accept方法。

public class TcpEchoClient {
	//客服端使用Socket对象和服务端建立连接
	private Socket socket = null;
	public TcpEchoClient(String serverIp, int serverPort) throws IOException {
		//和服务端建立连接需要知道服务端的IP,Port
		socket = new Socket(serverIp, serverPort);
	}
	public void start() {
		Scanner scanner = new Scanner(System.in);
		try(InputStream inputStream = socket.getInputStream();
			OutputStream outputStream = socket.getOutputStream()) {
			while(true) {
				//1.从控制台读取数据,构造请求
				System.out.print("->");
				String request = scanner.next();
				//2.发送请求给服务器
				PrintWriter printWriter = new PrintWriter(outputStream);
				printWriter.println(request);
				printWriter.flush();
				//3.从服务器读取响应
				Scanner respScanner = new Scanner(inputStream);
				String response = respScanner.next();
				//4.把响应显示到界面上
				System.out.println(response);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) throws IOException {
		TcpEchoClient client = new TcpEchoClient("127.0.0.1", 6060);
		client.start();
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows10腾讯文档下载和安装

文章目录 Windows10腾讯文档下载和安装官网下载执行安装 Windows10腾讯文档下载和安装 官网下载 官网 下载后: 执行安装 找到下载目录 安装后打开 扫描登录即可

Clickhouse学习笔记(9)—— 语法优化

ClickHouse 的 SQL 优化规则是基于 RBO(Rule Based Optimization)实现的 官方数据集的使用 为了方便测试CK的语法优化规则,尝试使用官方提供的数据集; 需要使用的数据集是visits_v1和hints_v1: Anonymized Web Analytics Data …

【大模型】大语言模型语料下载

文章目录 概述Hugging Faceobs操作git-lfs例子RedPajama-Data-1TSlimPajama-627B/git clone续传 数据格式参考资料 概述 大模型训练中语料是非常重要的,目前公网上有各种各样的语料可以供下载,但是不可能每个用户、每次训练任务都通过公网去拉取语料&am…

SparkSQL之Rule体系

在Unresolved LogicalPlan逻辑算子树的操作(如绑定、解析、优化等)中,主要方法都是基于规则(Rule)的,通过Scala语言模式匹配机制(Pattern-match)进行树结构的转换或节点改写。Rule是…

践行“微”使命——建行江门市分行大力发展普惠金融支持小微企业

促进小微企业发展是保持国民经济高质量发展的重要基础。建行广东省江门市分行立足当地特色产业之需,以金融活水润泽侨乡众多小微企业,携手共赴美好未来。 双向奔赴,添翼高企发展 江门开平水口镇是全国三大水暖卫浴生产基地之一,…

PDF文件编辑器有哪些?10 个适用的PDF 编辑器推荐!

PDF 编辑器始终配备简单的界面以及高效管理工作所需的所有功能。 那么如何选择合适的版本呢?在线工具还是离线工具更好? 为了帮助您回答这些问题,我将通过多次深入的测试来详细回顾十大免费编辑器。现在让我们来探索一下吧! 10 …

UDP协议

1.UDP协议的特点 无连接:不需要双方建立连接,直接就可以发送,就类似发短信,不需要知道对方是否在线,直接发送不可靠传输:无法知道对方是否有收到信息面向数据报:发送消息是以数据报尾单位全双工…

游戏交易平台系统源码下载 网络游戏币、装备、账号、道具等交易网站源码

最新仿7881游戏交易平台系统源码下载网络游戏币、装备、账号、道具等交易网站源码 下载地址:https://bbs.csdn.net/topics/617562568

数据结构与算法之排序: Leetcode 21. 合并两个有序链表 (Typescript版)

合并两个有序链表 https://leetcode.cn/problems/merge-two-sorted-lists/ 描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 …

最详细ChatGPT+AI绘画+企业知识库+视频去水印系统源码搭建流程,手把手教你搭建

一、系统介绍 这款源码搭载了强大的AI问答功能,是基于目前最强大AI大语言模型ChatGPT进行开发的Ai智能问答系统,并使用stablediffusion加最新的comfyui作为底层技术的绘画系统,使用comfyui的api接口,可以更灵活的定制自己的绘画工作流&#…

亲身体验告诉你:亚马逊云科技海外服务器是否值得一试?

前言 在当今数字化时代,云计算已经成为企业和个人发展的重要支撑。亚马逊云科技作为全球领先的云计算服务提供商,其海外服务器备受瞩目。然而,对于一些用户来说,是否值得一试亚马逊云科技的海外服务器仍然是一个疑问。本文将通过亲…

02. Python基础数据类型

1、前言 前面我们介绍了认识了Python以及Python的基础环境搭建,今天我们介绍下Python的一些基础语法。 2、Python基础 2.1、输入输出 2.1.1、输出 print() 用于输出指定的文字,括号中的为输出的字符串。print()也可以同时接收多个字符串,…

CHM Viewer Star 6.3.2(CHM文件阅读)

CHM Viewer Star 是一款适用于 Mac 平台的 CHM 文件阅读器软件,支持本地和远程 CHM 文件的打开和查看。它提供了直观易用的界面设计,支持多种浏览模式,如书籍模式、缩略图模式和文本模式等,并提供了丰富的功能和工具,如…

Linux各种版本安装详细步骤和root密码破解

文章目录 VMware新建虚拟机硬件设置设置虚拟网络挂载ISO文件 root密码破解 VMware新建虚拟机 硬件设置 设置虚拟网络 编辑>虚拟网络编辑器>VMnet8(NAT模式) 挂载ISO文件 加电>开启次虚拟机 第二项可以检查挂载上来的iso文件是否完整没有破坏 磁盘分区 选自定义分…

服务号如何升级订阅号

服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务,每月可推送4次,每次最多8篇文章,发送的消息直接显示在好友列表中。订阅号更侧重于信息传…

夯实思想根基:建行江门市分行持续加强党建工作

建行广东省江门市分行深化落实新时代党的建设总要求,坚持不懈用先进思想武装头脑和凝心铸魂,强化党建工作,夯实思想根基,护航高质量发展。 我是党员我先学 理论学习是党员的“永恒课题”。建行江门分行全体党员干部依托数字党建…

Web APIs——正则表达式使用

1、什么是正则表达式 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象 通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式 1.1 正则表达式使用场景 例如…

desc相关注入

desc相关注入 补充

Java算法(七):随机产生验证码 前后端验证码比对处理 实战思路步骤

Java算法(七) 随机产生验证码 package com.liujintao.random;import java.util.Random; import java.util.Scanner;public class RandomNumber {/*** 该函数调用验证码所有的函数,完成验证码模块功能开发* param args*/public static void …

某城高速综合管控大数据大屏可视化【可视化项目案例-04】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…