TCP和UDP的服务器与客户端构建

UDP网络协议与TCP网络协议的区别

UDP和TCP是传输层的两种重要的网络传输协议,她两有着相似之处,但也有着不同之处,在传输层的传输中各自起着不同的作用

主要概括

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

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

概念介绍

连接

这里的连接只是一个抽象的概念,就好比你和你老婆的结婚证,连接着你们彼此一样 。

对于TCP的连接而言,也是如此的抽象连接,A和B通信,A和B先建立连接,让A保存B的信息,B保存A的信息(彼此之间知道,谁和他建立了连接关系),简单点来说就是TCP的连接需要 "三次握手"后才确定你能发送数据,当对方断开连接就不能发送消息了

UDP是无连接的,他并不保存对方的信息,就是我发我的,不管对方是否连接,想发就发,不管对方状态怎么样。

可靠传输

TCP是可靠传输的,是网络层兜底的保障机制,也就是说它不会发送完数据就立即结束,而是等对方回复了"收到了"才会结束,而且如果数据发送的混乱了还会排好序在送到对方手上;

UDP是不可靠传输,传输出去的数据他就不管了,即使对方未接收到也停止发送了。

面向字节流和面向数据报

TCP使用的是面向字节流,面向字节流就是发送100个字节数据,可以向流水一样先 10个10个的发送也可以100个直接全部发送。

UDP是面向数据报发送的,也就是说UDP是以数据报的方式把每次发一个数据包的单位数据,接收方也是以一个数据报为单位的接收

全双工

TCP和UDP都为全双工,全双工的意思是发送数据和接收数据是可以一起完成的,还有一个半全双工,半全双工就和全双工相反,它只能完成发送数据或者接收数据,二者不能相互完成。

服务器的构建

UDP的服务器与客户端构建

首先讲讲UDP服务器与客户端的构建的两个类:DatagramSocket()和DatagramPacket()

DatagramSocket类有两个构造方法,第一个是无参版本的构造方法,这个无参版本的构造方法是代表让系统给你一个没有使用的端口,第二个是有参版本的,是自己指定一个端口

DatagramSocket主要有三个方法,第三个就无需多言了,关闭资源;receive方法呢是用来接收客户端发送过来的数据的(无数据传入时会产生堵塞),当然他拿到的数据就是二进制的;send方法就是发送数据的(无堵塞)。

这个DatagramPacket类的构造方法很多,这里先讲两个,第一个是new一个byte数组并指定int length的长度。第二个就是构造一个byte数组,并把目的端口的端口号和IP传给传给DatagramPacket

UDP的服务器构建

首先是这构造方法的构建,这里为什么要指定端口号呢?为了让客户端通过这个端口号找到这个服务器。

这个是这个服务器的核心代码,我们来一一讲解:

while(true)循环是为了让客户端与服务器二者能交互多次,而不是交互一次就结束。

复制代码
DatagramPacket requestPacker = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacker);

这个方法先构建了一个带有byte[]的对象,是为了让socket.receive(requestPacker)把接收到的数据传给requestPacker类中的byte[]里边去;前边我们也说过receive是用来接收客户端发送过来的请求的。

复制代码
String request = new String(requestPacker.getData(),0,requestPacker.getLength());

这个呢是把接收到的数据赋值给request,为什么要这样子呢?我们知道数据发送过来的是二进制数据,而我们要把二进制数据转换成字符串,这样赋值String可以把二进制的数据转换未字符串。requestPacker.getData()这个是读取数据的因为byte有可能会有很多空值全部接收会占空间所以使用了"0,requestPacker.getLength()"

复制代码
String response = process(request);
private String process(String request) {
    return request;
}

这个呢因为我们只是简单的做一个回声,所以直接返回,但如果想要增加业务的逻辑这里是最复杂的,服务器的业务也是在这里写的。

复制代码
DatagramPacket datagramPacket = newDatagramPacket(response.getBytes(),response.getBytes().length,
        requestPacker.getSocketAddress());

这个对象是DatagramPacket的另一种传参方式,response.getBytes()这个是把response的数据传给datagramPacket,response.getBytes().length这个是确保传的数据长度, requestPacker.getSocketAddress()这个是传入源端口和源IP,这里的源端口和源IP都是全给他的客户端的,因为我们是把数据传回去给他!!!

复制代码
System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacker.getAddress().toString(), requestPacker.getPort(),
        request, response);

最后这个就很简单了,就是打印一下客户端的IP和端口等

UDP客户端的构建

首先是构造方法的构建,因为客户端需要提供目的IP和要访问的服务器的目的端口,所以在这里要先把这两个给设计好,客户端的端口号就由系统随机产生一个就好了,毕竟客户端是给客户用的,没必要让用户输入,端口号重复造成没必要的问题。

这里呢是客户端的核心代码,下面我们一一解释:

while(true)死循环和服务器一样,让客户端和服务器连接一次即可重复使用

复制代码
System.out.println("请输入要发送的内容:");
if(!sc.hasNext()){
    break;
}
String request = sc.next();

这个呢就是让客户输入内容,如果客户没输入则退出。

复制代码
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length
        , InetAddress.getByName(serverIP), serverPort);

这个呢就是相当于把客户输入的数据打包,request.getBytes(), request.getBytes().length这个是获取用户输入的数据,并指定长度至有效数据,InetAddress.getByName(serverIP)这个是把目的IP传给requestPacket,serverPort这个是把目的端口传给requestPacket

复制代码
socket.send(requestPacket);

这个是把数据发送出去

复制代码
DatagramPacket datagramPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(datagramPacket);

这个是把接收数据并把数据传给byte[]数组。

复制代码
String str = new String(datagramPacket.getData(), 0, datagramPacket.getLength());

这个是把接收到的数据转化成字符串,转化的规定和服务器一样。

复制代码
System.out.println(str);

最后在打印接收的数据。

TCP的客户端与服务器的构建

TCP客户端与服务器的构建涉及到两个方法:ServerSocker和Socker两个方法,其中ServerSocker是提供给服务器使用的,Socker类是服务器和客户端共同使用的。

ServerSocker

ServerSocker这个方法的构造方法和UDP的DatagramSocket()有所不同,因为ServerSocker是提供给服务器使用的故而必须指定一个端口号。

ServerSocker构建服务器时有两个方法:accept和close,close方法就不再多言了,他是关闭资源的,减少不必要的开支;accept就和DatagramSocket()的receive差不多用来与客户端连接,返回一个Socker的数据。

Socker

Soker的构造方法呢主要用于客户端与服务器,他的构造方法主要用于客户端,传入服务器的IP和端口号,也就是目的IP和目的端口号。

Socker有以下三个方法,getInetAddress()、getINputStream和getOutStream三个方法,后两个个方法呢就是用于接收数据和传输数据的,第一个用到的时候在讲。

TCP服务器的构建TCP服务器的构造方法还是和UDP一样,指定一个端口号。

这个是使用服务器的方法

复制代码
ExecutorService executorService = Executors.newCachedThreadPool();

这个是使用了线程池,而这个线程池是没有指定核心数与非核心数等参数的,它代表着有客户端发起请求就会创建线程,当客户端的请求终止时线程也会跟着销毁。

while(true)死循环是为了让服务器能给多个客户端服务,而不是只共给几个人使用。

复制代码
Socket socket = serverSocket.accept();

这个呢是使用了ServerSocker的accept方法,用于接收客户端发送的请求。并以Socker来接收请求。

复制代码
executorService.submit(()->{
   processConnection(socket);
});

这个就很简单了,把请求传给 processConnection(socket);方法,这个方法也是服务器的核心代码, processConnection(socket);里边执行的内容在传给线程池executorService,执行代码。

这个processConnection是整个服务器的关键。下面我们一一解释:

复制代码
try(InputStream inqutStream = socket.getInputStream();
    OutputStream outputStream = socket.getOutputStream())

先解释解释为啥代码要放在try里边:这样即使我们不写close方法try也会帮我们执行,减少不必要的开销。

复制代码
InputStream inqutStream = socket.getInputStream();

这个对象的应用是用来接收发送过来的数据的,

复制代码
Scanner scanner = new Scanner(inqutStream);
String request = scanner.next();

通过这两个方法接收并把二进制数据转换成字符串传给request。

复制代码
OutputStream outputStream = socket.getOutputStream()

这个方法相当于打通了数据传输的通道,用于后续把数据发送出去。

复制代码
PrintWriter writer = new PrintWriter(outputStream);

这个方法将底层的字节输出流(OutputStream)包装成一个更方便写入文本的"打印写入器"。

配合它使用的方法还有

复制代码
writer.println(response);

这个方法是把**response** 字符串转换成字节(基于字符编码),写入内部缓冲区,但 不会立即发送出去 ,需要调用

复制代码
writer.flush();

这个方法来发送数据。

最后在通过finally关键字把socket.close();代码执行了关闭资源。

TCP客户端的构建

依旧是构造方法的的创建,前面我们说过Sokcer类可以直接传目的IP和目的端口号,故而直接在这里传入参数即可。

客户端的核心代码。

复制代码
try(InputStream inputStream = socket.getInputStream();
    OutputStream outputStream = socket.getOutputStream())

这个和服务器的搭建一样,为了有效的关闭资源和接收数据和打通传输数据的通道。

复制代码
Scanner scannerNet = new Scanner(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream);

这两个方法依旧和服务器的功能一样,一个是接收数据,一个是把传送的数据"打包"好。

复制代码
System.out.println("请输入数据:");
//输入要发送的数据 !!!
String request = scanner.next();
printWriter.println(request);
printWriter.flush();

需要注意的是客户端肯定是先发数据,服务器才能接收到数据然后发送的。

复制代码
String response = scannerNet.next();
System.out.println(response);

然后把接收到的数据传给response在打印出来。

客户端搭建完毕!!!!

相关推荐
FIT2CLOUD飞致云3 小时前
赛道第一!1Panel成功入选Gitee 2025年度开源项目
服务器·ai·开源·1panel
yanlou2333 小时前
[C++/Linux HTTP项目] HTTP服务器基于muduo高性能服务器搭载【深入详解】
运维·服务器·http·muduo库·http高性能服务器
天空属于哈夫克34 小时前
企微第三方 RPA API:非官方接口与官方接口的差异解析及选型建议
运维·服务器
niceffking4 小时前
linux 信号内核模型
linux·运维·服务器
残梦53144 小时前
Qt6.9.1起一个图片服务器(支持前端跨域请求,不支持上传,可扩展)
运维·服务器·开发语言·c++·qt
醒过来摸鱼4 小时前
Redis 服务器线程与事件循环解析
服务器·数据库·redis
hweiyu004 小时前
Linux 命令:paste
linux·运维·服务器
移动云开发者联盟5 小时前
一键部署!移动云全面上线Clawdbot
运维·服务器·负载均衡
upc8865 小时前
linux修改文件权限
linux·运维·服务器
祁鱼鱼鱼鱼鱼5 小时前
云原生-socat热更新工具
linux·运维·服务器