UDP和TCP的区别、网络编程(UDP回显服务器、TCP回显服务器)

news/2024/10/21 7:50:15/

目录

一、什么是网络编程

二、网络编程的内容概念

接受端和发送端

请求和响应

服务端和客户端

三、UDP和TCP协议的区别

四、UDP网络编程的类和函数(回显服务器)

DatagramSocket

DatagramPacket 

InetSocketAddress

基于UDP的回显服务器和客户端:

UDP回显服务器 

 UDP回显客户端

五、TCP网络编程的类和函数(回显服务器)

ServerSocket 

Socket

基于TCP的回显服务器和客户端:

TCP回显服务器

 Tcp回显客户端


一、什么是网络编程

⽹络上的主机,通过不同的进程,以编程的⽅式实现⽹络通信(或称为⽹络数据传输)。也就是客户端和服务器之间的通信。 

我们如果去xx视频看电视剧,也就是发出请求(我要看这个电视剧,请服务器给我响应),然后服务器就会给你这个视频的地址(响应),然后你就能看到电视剧了。 

当然,我们只要满⾜进程不同就⾏;所以即便是同⼀个主机,只要是不同进程,基于⽹络来传输数 据,也属于⽹络编程。 (由于设备有限,这里都用一个主机来示范)

如上图:

服务器就是个进程,客户端也是个进程。服务器为客户端提供数据。

二、网络编程的内容概念

接受端和发送端

在⼀次⽹络数据传输时: 

接收端收数据的一方,也就是网络通信的源主机。

发送端发数据的一方,也就是网络通信的目的主机。

请求和响应

获取⼀个⽹络资源,涉及到两次⽹络数据传输,如下: 

请求请求数据的发送。

响应响应数据的发送。

比如:你去一个餐馆,要点餐。

请求:你说老板我要一份酸菜鱼。响应:老板听完后做了份酸菜鱼 。

服务端和客户端

 在常⻅的⽹络数据传输场景下: 

服务端提供服务的⼀⽅进程,可以提供对外服务。

客户端获取服务的⼀⽅进程,称为客⼾端。

三、UDP和TCP协议的区别

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

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

有无连接这里的“连接”是一种抽象的连接,是绑定对方信息。若双方都绑定对方的信息,则是有连接,没有绑定对方的信息则是无连接。就像结婚证,一式两份上面都有自己和爱人的信息。

可不可靠传输这个也不是我们说这个人可不可靠的那种可靠,而是这个协议它有没有“尽可能”的去传输数据。就比如要传一个信息,可能会失败,这时候采取什么样的行动呢?如果可靠,它可能会进行重新发送啊,或者过一会儿发送什么什么的;但是不可靠传输只管传,其他什么都不会做,所以是不可靠传输。

面向字节流,面向数据报这里指的是传输的基本单位是什么,面向字节流的基本是字节流,而面向数据报的基本单位是数据报。两者不同,传输的方式也会不一样。

全双工同个时间内,A->B和B->A可以同步进行。而半双工则是同个时间,只能单向进行。

四、UDP网络编程的类和函数(回显服务器)

DatagramSocket

DatagramSocket 是UDP Socket,⽤于发送和接收UDP数据报。

DatagramSocket 构造⽅法:

方法签名方法说明
DatagramSocket()创建⼀个UDP数据报套接字的Socket,绑定到本机任 意⼀个随机端⼝(⼀般⽤于客⼾端)
DatagramSocket(int port)创建⼀个UDP数据报套接字的Socket,绑定到本机指 定的端⼝(⼀般⽤于服务端)

DatagramSocket ⽅法: 

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

DatagramPacket 

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

DatagramPacket 构造⽅法: 

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造⼀个DatagramPacket以⽤来接收数据报,接收的 数据保存在字节数组(第⼀个参数buf)中,接收指定 ⻓度(第⼆个参数length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造⼀个DatagramPacket以⽤来发送数据报,发送的 数据为字节数组(第⼀个参数buf)中,从0到指定⻓ 度(第⼆个参数length)。address指定⽬的主机的IP 和端⼝号

 DatagramPacket ⽅法:

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

构造UDP发送的数据报时,需要传⼊ SocketAddress ,该对象可以使⽤ InetSocketAddress 来创建。

InetSocketAddress

InetSocketAddress ( SocketAddress 的⼦类 )构造⽅法:

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

基于UDP的回显服务器和客户端:

UDP回显服务器 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;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);// 上述收到的数据, 是二进制 byte[] 的形式体现的. 后续代码如果要进行打印之类的处理操作// 需要转成字符串才好处理.String request=new String(requestPacket.getData(),0,requestPacket.getLength());// 2. 根据请求计算响应, 由于此处是回显服务器. 响应就是请求.String respond=process(request);// 3. 把响应写回到客户端DatagramPacket respondPacket =new DatagramPacket(respond.getBytes(),0,respond.getBytes().length,requestPacket.getSocketAddress());socket.send(respondPacket);// 4. 把日志打印一下.System.out.printf("[%s:%d] req=%s,res=%s\n",requestPacket.getAddress(),requestPacket.getPort(),request,respond);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer=new UdpEchoServer(9090);udpEchoServer.start();}
}

1)用DatagramSocket创建出一个UDP服务器,参数为端口号(比如9090,就是约定客户端找到服务器的地方,就像约会地点);这里不需要ip地址,因为每个机子才有ip地址,所以直接在客户端new一个对象,参数输入服务器的ip地址就行了。

2)开始服务器,由于服务器基本上都是7*24小时运行的,所以我们用个while循环来一直给它运行;

3)我们用DatagramPacket来接受服务器传来的数据报,传满这个byte数组,然后包装成DatagramPacket对象,让服务器来接收;

4)我们现在是学习,为了方便查看里面的数据,所以我们把数据报转成字符串,给服务器去响应。

5)写一个process响应函数(回显服务器,其实就是return客户端发来的信息),来响应客户端发来的信息,这里我们就把刚刚的字符串传进去,然后返回同样的数据。

6)把这个返回来的信息,我们拿DatagramPacket来包装起来,发回给客户端,这样就完成了响应。(注意的是,发回去的时候要传客户端的ip和端口号,不然都不知道要传给谁)

7)打印日志。

 UDP回显客户端

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;private int serverPort;public UdpEchoClient(String serverIP, int serverPort) throws SocketException {socket = new DatagramSocket();this.serverIP = serverIP;this.serverPort = serverPort;}private void start() throws IOException {System.out.println("启动客户端");// 1. 从控制台读取到用户的输入.Scanner scanner = new Scanner(System.in);while (true) {System.out.print("->");String request = scanner.next();// 2. 构造出一个 UDP 请求, 发送给服务器.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIP), this.serverPort);socket.send(requestPacket);// 3. 从服务器读取到响应DatagramPacket respondPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(respondPacket);String respond = new String(respondPacket.getData(), 0, respondPacket.getLength());// 4. 把响应打印到控制台上.System.out.println(respond);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);udpEchoClient.start();}
}

1)我们UDP的客户端的构造函数是要有服务器IP和端口号的,因为客户端要主动发数据报给服务器,但是new客户端对象的时候却不需要,因为服务器最开始不需要主动找客户端,而且指定端口号的话,可能会和客户的电脑中的端口号冲突,就会产生bug;

2)因为客户端可能7*24小时都有人发数据,所以我们也是做一个while循环,输入字符串,这时候我们构建DatagramPacket来接受一下,所以这个字符串传进来的时候要getByte()一下,然后send发送这个datagrampacket对象;

3)然后等待响应,接收服务器传来的响应,也是用DatagramPacket来接收;

4)为了方便观看,也是转成字符串,打印出来。

五、TCP网络编程的类和函数(回显服务器)

ServerSocket 

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

ServerSocket 构造⽅法: 

方法签名方法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端⼝

 ServerSocket ⽅法:

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

因为TCP是有连接的,所以需要accept来连接一下,但这里的连接是抽象的连接(这里的连接我们是察觉不到的,是系统内核做的),这里的accept类似于做了一个“确认连接”的作用。

就像打电话,A打给B,会经历一系列的数据传输(比如给基站发数据说我要大给B,但这里我们是察觉不到的,很快B就响铃了)这时B接通了,就相当于accept连接完成了。

Socket就像去揽客,,然后accept连接出来的是一个客户端对象(网卡),把它揽进来,相当于我们对它进行一对一的服务。

Socket

Socket 是客⼾端Socket,或服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服 务端Socket。

不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据的。

Socket 构造⽅法:

方法签名方法说明
Socket(String host, int port)创建⼀个客⼾端流套接字Socket,并与对应IP的主机 上,对应端⼝的进程建⽴连接

Socket ⽅法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输⼊流
OutputStream getOutputStream()返回此套接字的输出流

基于TCP的回显服务器和客户端:

TCP回显服务器

import java.io.*;
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("启动服务器!");ExecutorService service= Executors.newCachedThreadPool();while (true){Socket clientSocket=serverSocket.accept();//不止可以创建线程,还可以使用线程池
//            Thread t=new Thread(()->{
//                processConnection(clientSocket);
//            });
//            t.start();//可以使用线程池,效率更高service.submit(()->{processConnection(clientSocket);});}}// 针对一个连接, 提供处理逻辑private void processConnection(Socket clientSocket) {// 先打印一下客户端的信息System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());// 获取到 socket 中持有的流对象.try(InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream()) {// 使用 Scanner 包装一下 inputStream. 就可以更方便的读取这里的请求数据了.Scanner scanner=new Scanner(inputStream);PrintWriter writer=new PrintWriter(outputStream);while (true){// 1. 读取请求并解析if (!scanner.hasNext()){// 如果 scanner 无法读取出数据, 说明客户端关闭了连接, 导致服务器这边读取到 "末尾"break;}String request=scanner.next();// 2. 根据请求计算响应String respond=process(request);//3. 把响应写回给客户端// 此处可以按照字节数组直接来写, 也可以有另外一种写法.// outputStream.write(response.getBytes());writer.println(respond);writer.flush();// 4. 打印日志System.out.printf("[%s:%d],req=%s res=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,respond);}} catch (IOException e) {e.printStackTrace();}System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server=new TcpEchoServer(9090);server.start();}
}

 1)用ServerSocket创建出一个TCP服务器,参数为端口号(比如9090,就是约定客户端找到服务器的地方,就像约会地点),这里不需要ip地址,因为每个机子才有ip地址,所以直接在客户端new一个对象,参数输入服务器的ip地址就行了。

2)因为服务器都是7*24小时运行的,所以我们用while循环把他们框起来,然后因为客户很多,频繁的创建和开销很消耗资源,所以我们考虑使用线程池来解决这个问题,注意这里使用的是newCachedThreadPool(),因为这个方法的线程池数量很大,21亿可以解决这个问题。

3)我们通过服务器的accept()方法来接收客户端对象,用Socket接收,得到的就是操作客户端的网卡;

4)因为TCP是面向字节流的,所以我们使用IO操作,来获取这些字节流,为了方便,我们把inputstream对象读到的字节传到scanner里面,这样非常的方便读取。同理outputstream作为参数传入printwriter里面,为什么用这个呢,因为这个输出的时候是需要结尾为空白符作为结束标志的,而printwriter有个方法println,会有\n作为结束标志,就不要手动打\n了。这样数据流就取一个完整的数据流了,就不会和下一个数据流混一起了。

5)其余的看看源代码,其实也差不多和UDP类似,就那么一些出入而已。

6)flush()是“冲刷缓存区”,IO操作时间长,很消耗资源,所以内核优化了一下,得攒一堆IO才会一起发送,不然的话就会卡着,发不出去。

7)打印日志

 Tcp回显客户端

import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket clientSocket=null;public TcpEchoClient(String serverIP,int serverPort) throws IOException {clientSocket=new Socket(serverIP,serverPort);}public void start(){System.out.println("客户端启动!");try(InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream()) {Scanner scanner=new Scanner(inputStream);Scanner scannerIn=new Scanner(System.in);PrintWriter writer=new PrintWriter(outputStream);while (true){// 1. 从控制台读取到用户的输入.System.out.print("->");String request=scannerIn.next();// 2. 构造出一个 UDP 请求, 发送给服务器.writer.println(request);writer.flush();// 3. 从服务器读取响应if(!scanner.hasNext()){break;}String respond=scanner.next();// 4. 打印响应结果System.out.println(respond);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);tcpEchoClient.start();}
}

1)我们UDP的客户端的构造函数是要有服务器IP和端口号的,因为客户端要主动发数据报给服务器,但是new客户端对象的时候却不需要,因为服务器最开始不需要找客户端,而且指定端口号的话,可能会和客户的电脑中的端口号冲突,就会产生bug。

2)接下来的基本上与前面几个同理,看看代码,这里就不赘述了。


http://www.ppmy.cn/news/1538298.html

相关文章

电脑查不到IP地址是什么原因?怎么解决

在日常使用电脑的过程中,有时会遇到无法查询到电脑IP地址的情况,这可能会影响到网络的正常使用。本文将探讨电脑查不到IP地址的可能原因,并提供相应的解决方案。 一、原因分析 ‌网络连接问题‌:首先,网络连接不稳定或…

华为高频手撕冲刺

简单题 两数之和 方法一,暴力破解,时间复杂度O(n^2),空间复杂度O(1) class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:nlen(nums)for i in range(n):for j in range(i1,n):if nums[i]nums[j]target:retur…

Meta 发布 Quest 3S 头显及 AR 眼镜原型:开启未来交互新视界

简介 在科技的浪潮中,Meta 始终站在创新的前沿,不断为我们带来令人惊叹的虚拟现实和增强现实体验。2024 年 10 月 6 日,让我们一同聚焦 Meta 最新发布的 Quest 3S 头显及 AR 眼镜原型(Orion),探索这两款产品…

leetcode 3217 从链表中移除在数组中的结点

1.题目要求: 给你一个整数数组 nums 和一个链表的头节点 head。从链表中移除所有存在于 nums 中的节点后,返回修改后的链表的头节点。 示例 1: 输入: nums [1,2,3], head [1,2,3,4,5] 输出: [4,5] 解释: 移除数值…

Spring Cloud Netflix Eureka 注册中心讲解和案例示范

在微服务架构中,服务的发现和注册是至关重要的一环。Netflix Eureka 是一个在云端设计的服务注册与发现系统。它允许各个微服务将自身注册到注册中心,并在需要时发现其他服务,从而实现客户端负载均衡、服务容错以及动态扩展。本文将深入分析 …

【C++】拆分详解 - vector

文章目录 一、vector的介绍二、vector的使用1. 构造2. 迭代器3. vector 空间增长问题4. 增删查改5. vector 迭代器失效问题5.1 底层空间改变(扩容、缩容)5.2 指定位置元素的删除操作5.3 Linux与VS平台差异 三、vector 模拟实现0. 整体框架1. 构造 / 析构…

0基础能不能转行做网络安全?

0基础能不能转行做网络安全?网络安全人才发展路线 最近有同学在后台留言,0基础怎么学网络安全?0基础可以转行做网络安全吗?以前也碰到过类似的问题,想了想,今天简单写一下。 我的回答是先了解&#xff0c…

华为公有云实战

1.申请一台ECS云主机,并且可以提供web服务 1.1访问云主机-华为特有技术novnc,KVM中提到vnc技术,novnc是不用安装vnc客户端用浏览器html语言实现。 1.2cloudshell 1.3小工具 ssh 弹性ip 1.4.安装httpd服务 建立索引文件 浏览器上输入弹性ip可…