当前位置: 首页 > news >正文

[网络编程】网络编程的基础使用

系列文章目录

1、 初识网络


网络编程套接字

  • 系列文章目录
  • 前言
  • 一、TCP和UDP协议的引入
  • 二、UDP网络编程
    • 1.Java中的UDP
    • 2.UDP回显代码案例
    • 3.UDP网络编程的注意事项
  • 三、TCP网络编程
    • 1.TCP回显代码案例
    • 2.TCP多线程使用
  • 总结


前言

在学习完基础的网络知识后,完成跨主机通信的网络编程。


一、TCP和UDP协议的引入

注意:TCP协议并非TCP/IP模型。
TCP和UDP都属于传输层协议中的一种,为应用层提供服务。但是二者之间的特点和差异都很大,因此有了两套API分别应用于二者。
特点:
TCP:有连接,可靠传输,面向字节流,全双工
UDP:无连接、不可靠传输、面向数据报、全双工

其中有连接的意思是如同打电话一样,在电话接通以后才能相互通信;无连接就像发送短信,不需要“先接通”,允许直接发送。
可靠传输的意思是将要传输的数据尽可能传输给对方,不可靠传输的意思是在数据传输时不关心对方是否收到信息。
面向字节流,和文件操作中的字节流类似,读写操作十分灵活。
面向数据报:在UDP协议中,传输数据的基本单位时一个个UDP数据报,一次读写只能读写一个完整的UDP数据报。
全双工:在一条链路中能够进行双向通信;半双工:一条链路中只能进行单向通信。

二、UDP网络编程

1.Java中的UDP

在Java中,引入系统的API(称为Socket API)并进行封装,在Java中主要有两个类:DatagramSocket和DatagramPacket。
1)DatagramSocket:socke可以理解为是一种文件,socket文件可以视为对网卡的硬件设备进行的抽象化。在UDP中对于操作系统中socket概念的封装,针对该对象进行读写操作,也可以理解为对网卡硬件设备的操作。
2)DatagramPacket: 针对UDP数据报的一个抽象表示,一个DatagramPacket对象,就相当于一个UDP数据报。一次发送/接收就相当于传输了一个DatagramPacket对象。

2.UDP回显代码案例

所谓回显代码,即客户端请求什么内容,服务器就返回什么代码。回显代码中没有实现复杂的逻辑,能够最简单的反映UDP的服务器和客户端之间的互动情况。

  1. UDPEchoServer:在Server端,端口号是固定的,DaragramSocket 在构造方法中传入端口号。在服务器启动之后需要不断接收请求和返回请求值。
    在DatagramSocket中的receive方法负责接收客户端发送过来的请求, 将接收到的数据转换成字符串request,作为后面处理请求的参数。通过方法process运行之后返回的结果需要数据报进行封装,因此在结果返回之后,新建一个数据报对象并将字符串结果转换成字节流。通过socket.send方法发送。
public class UdpEchoServer {DatagramSocket socket = null;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);String request = new String(requestPacket.getData(),0,requestPacket.getLength());String response = this.process(request);DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);System.out.printf("[%s:%d] req = %s,res = %s \n",requestPacket.getAddress(),requestPacket.getPort(),request,response);}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
  1. UDPEchoClient:在客户端,用户发起请求并接收返回的结果。对服务器发送的信息应该包括用户IP地址、用户端口号的信息。通过DatagramSocket类中的send方法发送请求。
    在start()方法中,新建字符串request来保存用户所发送的请求,由于网卡的的性质需要发送数据报格式。为了解决这个问题,我们需要new一个数据报requestPacket对请求内容进行封装。同时我们需要把字符串的数据转换成字节流的数据。
    INetAddress中的getByName方法中存储着服务器的IP地址以及服务器的端口号。
    在发送请求之后等待数据接收状态下,receive()方法进入阻塞状态。
    在接收到数据之后,将字节流数据转换成字符串的形式,用response接收。
public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;public UdpEchoClient(String serverIp, int serverPort) throws SocketException {this.socket = new DatagramSocket();this.serverIp = serverIp;this.serverPort = serverPort;}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(),0,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.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);client.start();}
}

最终,我们成功完成了UDP中基础的数据请求与通信。


3.UDP网络编程的注意事项

  1. 在上面的UDP编程中,主要可以分为以下几个步骤(如下图所示)
    在这里插入图片描述
  2. 进程的运行顺序是先启动服务器,再启动客户端。
  3. 在代码编写过程中,都没有涉及到close操作的原因:DatagreamSocket作为这个进程的对象和服务器程序的生命周期是一样的。如果在某个程序中,socket对象的生命周期和进程不一样,需要提前释放的话就需要进行close操作。

三、TCP网络编程

在TCP中的socket api中与UDP不同的是,在TCP中ServerSocket类是专门给服务器使用的。 Socket类是专门作用于客户端和服务器之间进行通信使用的。
由于TCP属于有连接和可靠传输,他们之间必须时刻保持通信状态。在ServerSocket中有accept方法接收客户端发来的请求。

1.TCP回显代码案例

我们知道TCP是面向字节流的,因此在TCP中的操作是以流对象进行操作的。

  1. 在Socket类中,通过两个方法:getInputStream和getOutpurStream 进行操作的。
  2. 在服务器中,start方法中通过accept同意客户端连接。接下来调用processConnection
  3. 使用Scanner读取InputStream流中的数据内容。
  4. 使用Scanner的好处是对于客户端传输进来的字节流,Scanner会自动将这些数据转换成String。
  5. 在方法start中,循环保持与客户端之间的通信。通过scanner.hasNext()方法判断是否还有输入,当客户端关闭了连接之后,服务器的输入流会收到一个EOF信号,从而判断出没有输入了,最终结束循环。
  6. 将回显的结果以response的结果以二进制的形式写入outputStream中。
  7. 最后,处理完client的请求之后,客户端下线之后,回收client对象,当下次重新有新的客户端时重新启用。
public void start() throws IOException {System.out.println("服务器启动!");//accept可以阻塞等待Socket socketClient = serverSocket.accept();processConnection(socketClient);}private void processConnection(Socket socketClient) throws IOException {System.out.printf("[%s:%d] 客户端上线\n", socketClient.getInetAddress(),socketClient.getPort());try(InputStream inputStream = socketClient.getInputStream();OutputStream outputStream = socketClient.getOutputStream()) {Scanner scanner = new Scanner(inputStream);while (true) {if (!scanner.hasNext()) {System.out.printf("[%s:%d] 客户端下线\n", socketClient.getInetAddress(),socketClient.getPort());break;}//接收客户端发送过来的请求String request = scanner.next();//处理请求String response = process(request);//把响应写回客户端outputStream.write(response.getBytes(),0,response.getBytes().length);//服务器打印日志System.out.printf("[%s:%d] req = %s resp = %s",socketClient.getInetAddress(),socketClient.getPort(),request,response);}} catch (IOException e) {throw new RuntimeException(e);}finally {socketClient.close();}}

在客户端方面,通过scannerNetWork读取inputStream中的数据。将请求的内容以二进制的形式写入outputStream中。当服务器关闭后,scannerNetWork传入服务器的EOF信号,代表着双方连接中断,因此可以退出循环结束进程。

public void start() {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {Scanner scannerNetWork = new Scanner(inputStream);while (true) {System.out.println("输入要发送的数据");//1.读取数据String request = scanner.next();//2.将request作为请求发送到服务器中,通过\n标识请求内容request += "\n";outputStream.write(request.getBytes());//3.读取服务器返回的响应if (!scannerNetWork.hasNext()){break;}String response = scannerNetWork.next();System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}

2.TCP多线程使用

在上面的TCP回显代码中,并不能使用于多线程的形势下。在服务器中,应当有线程池之类的多线程状态中,同时处理多个客户端发送过来的请求。
因此需要对服务器实现多线程解决问题。通过线程池的方式,while循环中不断接收客户端发来的连接请求,并将任务交予线程池解决。

public void start() throws IOException {System.out.println("服务器启动!");ExecutorService pool = Executors.newCachedThreadPool();//accept可以阻塞等待while (true) {Socket socketClient = serverSocket.accept();pool.submit(new Runnable() {@Overridepublic void run() {try{processConnection(socketClient);} catch (IOException e) {throw new RuntimeException(e);}}});}}

总结

在本文中主要讲解了TCP和UDP两个协议之间的区别;同时对UDP网络编程进行了最基础的创建。
源码☞本文源码

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Drools】(二)基于业务需求动态生成 DRL 规则文件:事实与动作定义详解
  • Apache ShardingSphere Proxy5.5.0实现MySQL分库分表与读写分离
  • Halcon学习之边缘扩展
  • Java代理模式详解
  • React 的 KeepAlive 实战指南:深度解析组件缓存机制
  • 【网络爬虫技术】(1·绪论)
  • 深度学习高效性网络
  • 2024钉钉杯B题医疗门诊患者及用药数据案例分析
  • SolidWorks设计库的应用
  • 基于Golang+Vue3快速搭建的博客系统
  • 顺序表和单链表的代码实现
  • Ubuntu22.04安装Go语言的几种方式
  • Nginx系列-12 Nginx使用Lua脚本进行JWT校验
  • 【第三天】计算机网络 HTTP请求中常见的状态码 什么是强缓存和协商缓存
  • Spark进化论:从RDD到DataFrame,揭秘Spark SQL如何成为性能引擎的幕后英雄
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • Golang-长连接-状态推送
  • HTML中设置input等文本框为不可操作
  • IndexedDB
  • JavaScript 一些 DOM 的知识点
  • Java多线程(4):使用线程池执行定时任务
  • leetcode98. Validate Binary Search Tree
  • Linux链接文件
  • MD5加密原理解析及OC版原理实现
  • Netty 4.1 源代码学习:线程模型
  • nfs客户端进程变D,延伸linux的lock
  • Otto开发初探——微服务依赖管理新利器
  • PAT A1120
  • PHP 的 SAPI 是个什么东西
  • Python学习之路13-记分
  • vuex 笔记整理
  • 阿里研究院入选中国企业智库系统影响力榜
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 聊聊redis的数据结构的应用
  • 普通函数和构造函数的区别
  • 一份游戏开发学习路线
  • 再谈express与koa的对比
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ‌‌雅诗兰黛、‌‌兰蔻等美妆大品牌的营销策略是什么?
  • #APPINVENTOR学习记录
  • #etcd#安装时出错
  • #stm32整理(一)flash读写
  • #传输# #传输数据判断#
  • (02)Hive SQL编译成MapReduce任务的过程
  • (1)svelte 教程:hello world
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (算法)区间调度问题
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**