【JavaEE】UDP、TCP的API介绍

server/2024/12/27 2:32:24/

目录

UDP数据报套接字编程

DatagramSocket API

DatagramPacket API

回显C/S示例

TIPS

TCP

ServerSocket API

Socket API

回显C/S示例


UDP数据报套接字编程

DatagramSocket API

        socket是操作系统中的一种概念,本质上是一种特殊的文件,socket属于是把“网卡”这个设备给抽象成文件,往socket文件中写数据,就相当于通过网卡发送数据,往socket文件读数据,就相当于通过网卡接收数据,在java中使用DatagramSocket来表示系统内部的socket文件。

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

DatagramSocket构造方法:

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

DatagramSocket方法:

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

DatagramPacket API

DatagramPacket表示一个udp数据报

DatagramPacket构造方法

方法说明
DatagramPacket(byte[] buf,int length)构建一个DatagramPacket以用来接收数据报,接收的数组保存在字节数组(第一个参数buf)中,第二个参数 结束指定长度
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构建一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf),从0到指定长度(第二个参数length),address指定目的主机的ip和端口号

 DatagramPacket方法

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

服务器和客户端都需要创建Socket对象,但是服务器的socket一般要显示手动指定一个端口号,而客户端的socket一般不能显式指定(不显式指定,此时系统会自动分配一个随机的端口),指定的话可能会与其他客户端冲突,系统自动分配可以避免冲突。

回显C/S示例

服务端代码

java">package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket=null;//指定服务器socket要绑定的端口private static final int PORT=9090;public UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(PORT);}public void start() throws IOException {System.out.println("服务器启动!");while(true){DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//当完成receive之后,数据都是以二进制的形式存储在DatagramPacket中//因此我们需要先将这个二进制数据转成字符String request=new String(requestPacket.getData(),0,requestPacket.getLength());String response=process(request);DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);System.out.printf("[%s:%d] req=%s, resp=%s\n",requestPacket.getAddress().toString(),responsePacket.getPort(),request,response);}}public String process(String request){return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer=new UdpEchoServer(9090);udpEchoServer.start();}
}

客户端代码

java">package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket=null;private String serverIP="";private int serverPort=0;public UdpEchoClient(String ip,int port) throws SocketException {socket=new DatagramSocket();serverIP=ip;serverPort=port;}public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner=new Scanner(System.in);while(true){System.out.println("-> ");String request=scanner.next();DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIP),serverPort);socket.send(requestPacket);DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response=new String(responsePacket.getData(),0,responsePacket.getLength());System.out.printf("%s\n",response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient=new UdpEchoClient("127.0.0.1",9090);udpEchoClient.start();}
}

效果如下: 

TIPS

1、new byte[4096],这个对象用来承载从网卡这边读到的数据,收到数据的时候,需要搞一个内存空间来保存这个数据,DatagramPacket内部不能自行分配内存空间,因此就需要我们手动把空间创建好,交给DatagramPacket进行处理。

2、服务器一旦启动,就会立即执行到这里的receive方法,此时,如果客户端的请求可能还没来呢,这种情况也没关系,receive就会直接阻塞,就会一直阻塞到客户端把请求发过来为止。

3、完成receive之后,数据都是以二进制的形式存储到DatagramPacket中,要想把这里的数据给显示出来,还需要把这个二进制数据给转成字符串

String request = new String(requestPacket.getData(),0,requestPacket.getLength());

  • 将0,requestPacket.getLength()区间内的字节,构造成一个String

4、response.getBytes().length()不能写成 response.length(),这涉及到字符集的问题,如果这个字符串的内容都是英文字符,此时字节和字符个数是一样的,但是如果包含中文,就不一样了 ,在网络传输时,都是以字节为单位

TCP

因为tcp是面向字节流的,传输的基本单位是byte,因此不需要像udp一样需要有一个数据报类表示传输的基本单位。

ServerSocket API

ServerSocket构造方法:

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

ServerSocket方法:

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

Socket API

Socket构造方法:

方法说明
Socket(String host,int port)创建一个客户端流套接字Socket,并与对应ip的主机上,对应端口的进程建立连接

Socket方法: 

方法说明
getPort()获取对端的端口
getInetAddress()获取对端的ip
getLocalAddress()获取本地的ip
getLocalPort()获取本地的端口
InputStream getInputStream()返回此套接字的输入流(进行read操作,就是接收)
OutputStream getOutputStream()返回此套接字的输出流(进行write操作,就是发送)

前面udp中DatagramSocket和ServerSocket没写close,是因为程序中,只有这么一个对象,生命周期是贯穿整个程序的,而在服务端accept,与客户端连接,在服务端每次有新的客户端来建立连接,都会创建一个新的clientSocket,每个socket对象会占据文件描述符表的位置,因此我们要记得释放。

回显C/S示例

服务端代码:

        这里服务端处理连接时创建新线程处理一个客户端连接,因为tcp连接服务端和客户端交互时,需要保持连接,如果不新建线程进行处理,客户端不能多个同时与服务端进行交互,需要排队等待。

        为了减少线程创建销毁的开销,这里引入线程池的使用。

java">package Tcp;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;
import java.util.concurrent.ThreadPoolExecutor;public class TcpServer {private ServerSocket serverSocket=null;public TcpServer(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();service.submit(new Runnable() {@Overridepublic void run() {try {proccessConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}public void proccessConnection(Socket clientSocket) throws IOException {//进入方法,先打印一个日志,表示当前有客户但上线了System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort() );//接下来进行信息交互try(InputStream inputStream=clientSocket.getInputStream()){OutputStream outputStream=clientSocket.getOutputStream();Scanner scanner = new Scanner(inputStream);while(true) {if (!scanner.hasNext()){System.out.printf("[%s:%d] 客户端下线了\n",clientSocket.getInetAddress(),clientSocket.getPort());break;}//1、请求并解析String request=scanner.next();//2、根据请求,计算响应String response=proccess(request);PrintWriter printWriter=new PrintWriter(outputStream);printWriter.println(response);//此处要记得刷新缓冲区,如果没有刷新操作,数据可能还在内存中,没有被写入网卡printWriter.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 {clientSocket.close();}}public String proccess(String request){return request;}public static void main(String[] args) throws IOException {TcpServer tcpServer=new TcpServer(9090);tcpServer.start();}
}

客户端代码:

javascript">package Tcp;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 TcpClient {private Socket clientSocket=null;public TcpClient(String serverIP,int port) throws IOException {clientSocket=new Socket(serverIP,port);}public void start(){Scanner scaner=new Scanner(System.in);try(InputStream inputStream=clientSocket.getInputStream()){OutputStream outputStream=clientSocket.getOutputStream();PrintWriter printWriter=new PrintWriter(outputStream);Scanner scannerNetwork=new Scanner(inputStream);while(true){//从控制台获取用户输入的内容System.out.println("-> ");String request=scaner.next();//把字符串作为请求,发送给服务器printWriter.println(request);printWriter.flush();//读取服务器返回的响应String response=scannerNetwork.next();System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpClient tcpClient=new TcpClient("127.0.0.1",9090);tcpClient.start();}
}


http://www.ppmy.cn/server/149850.html

相关文章

网络通信技术

网络通信技术 IP路由基础 什么是路由 路由是指导报文转发的路径信息,通过路由可以确认转发IP报文的路径。路由设备是依据路由转发报文到目的网段的网络设备,最常见的路由设备:路由器。路由设备维护着一张路由表,保存着路由信息。路由的功能 路径选择数据转发、数据过滤维…

golang实现简单的reids服务2

golang实现redis兼容的redis服务实现redis兼容的redis服务思路 golang实现redis兼容的redis服务 之前做的redis服务是通过tcp封装的自定义协议 原版项目地址:https://github.com/dengjiayue/my-redis.git 那么能不能实现一个redis兼容的redis服务,这样一般的redis包也可以调…

Shell语言基础语法(2)

目录 一、数据类型 1、数字类型 2、字符串类型 3、数组类型 (1)、普通数组 普通数组的定义 普通数组的访问 (2)、关联数组 关联数组的定义 关联数组的访问 二、变量值相关操作 1、获取变量长度 2、切片 3、截断 4、le…

21. 反射

一、什么是反射 Python 是一种动态语言,而反射(reflection)机制被视为动态语言的关键。反射机制指的是在程序的运行过程中,对于任意一个类,都可以知道这个类的所有属性和方法,对于任意一个对象,…

校园一卡通密钥管理系统基于 SSM 的高效架构搭建策略

第2章 开发环境与技术 开发校园一卡通密钥管理系统需要搭建编程的环境,也需要通过调查,对各个相关技术进行分析,选取适合本系统开发的技术与工具。 2.1 MYSQL数据库 题目确定了是一个应用程序之后,就开始按部就班的进行设计与分析…

小程序-基于java+SSM+Vue的校园水电费管理小程序设计与实现

项目运行 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

C++:列表初始化

一:C98 在c98中可以用花括号{}对数组和结构体进行初始化,比如: struct A {int a;int b; }; int main() {int a[3] { 1,2,3 };A b { 1,2 };return 0; } 二:C11 c11中扩大了使用范围,所以自定义类型也可以使用了。使用…

MinerU:PDF文档提取工具

目录 docker一键启动本地配置下载模型权重文件demo.py使用命令行启动GPU使用情况 wget https://github.com/opendatalab/MinerU/raw/master/Dockerfile docker build -t mineru:latest .docker一键启动 有点问题,晚点更新 本地配置 就是在Python环境中配置依赖和…