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

网络编程及原理(三)

目录

一 . Socket 套接字 

(1)流套接字 

(2)数据报套接字 

(3)原始套接字 

二 . 基于 UDP 协议实现回显服务器 / 客户端 

三 . 基于 TCP 协议实现回显服务器 / 客户端 


一 . Socket 套接字 

Socket 套接字是由系统提供用于网络通信的技术,是基于 TCP / IP 协议的网络通信的基本操作单元,基于 Socket 套接字的网络程序开发就是网络编程。

Socket 套接字主要针对传输层协议划分如下三类:

(1)流套接字 

流套接字:使用传输层 TCP 协议,TCP 即 Trasmission Control Protocol(传输控制协议),传输层协议。

TCP 的特点:有连接,可靠传输,面向字节流,有接收缓冲区,有发送缓冲区,大小不限,全双工。

对于字节流来说,也就是传输数据是基于 IO 流的,流式数据的特征就是在 IO 流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

这样表述可能很抽象,举个例子:字节流就像水流一样:

如果我们想要接 100 ml 的水,我们可以一次性接 100 ml ,也可以分 2 次,每次接 50 ml ,也可以分 10 次,每次接 10 ml 。

同样,我们想要从文件中读写 100 个字节,我们可以一次性读写 100 个字节,也可以分 2 次,每次读写 50 个字节,也可以分 10 次,读写 10 个字节。

这就是 “ 字节流 ” 。

(2)数据报套接字 

数据报套接字:使用传输层 UDP 协议,UDP 即 User Datagram Protocol (用户数据报协议),传输层协议。

UDP 的特点:无连接,不可靠传输,面向数据报,有接收缓冲区,无发送缓冲区,大小受限,一次最多传输 64 k, 全双工。

对于数据报来说,传输数据是一块一块的,发送一块数据假如为 100 个字节,必须一次性发送,接收也必须一次性接收 100 个字节,而不能像字节流那样分成多次。

(3)原始套接字 

原始套接字用于自定义传输层协议,用于读写内核没有处理的 IP 协议数据。

原始套接字这一块我们不用做过多了解。

二 . 基于 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 对象完成)socket = new DatagramSocket(port);}//通过 start 启动服务器的核心流程public void start() throws IOException {System.out.println("服务器启动!!!");while(true){//此处通过 “死循环” 不停地处理客户端请求// 1. 读取客户端请求并解析// receive: 从网卡上读取数据//如果网卡上收到了数据,receive 立即返回,获取收到的数据//如果网卡上没有收到数据,此时 receive 就会阻塞等待,一直等待到收到数据为止DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//上述收到的数据,是二进制 byte[] 的形式体现的,后续代码如果要进行打印之类的处理操作//需要转成字符串才好处理//取出字节数:String request = new String(requestPacket.getData(),0, requestPacket.getLength());// 2 . 根据请求计算响应(由于此处是回显服务器,响应就是请求)String response = process(request);// 3 . 把响应写回到客户端DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);// 4 . 打印日志System.out.printf("[%s:%d] req = %s,resp = %s\n",requestPacket.getAddress(),requestPacket.getPort(),request,response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
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 serverTP,int serverPort) throws SocketException {//不能指定端口号//客户端是主动的一方,不需要让服务器来找它,所以不用指定端口号//不指定端口号不代表没有端口号,而是客户端这边的端口号是系统自动分配了的//还有一方面,由于客户端是在用户的电脑上运行的,天知道用户的电脑上都有哪些程序,已经占用了哪些端口号socket = new DatagramSocket();this.serverIP = serverTP;this.serverPort = serverPort;}public void start() throws IOException {System.out.println("客户端启动!!!");Scanner scan = new Scanner(System.in);while(true){// 1 . 从控制台读取到用户的输入System.out.println(" -> ");String request = scan.next();// 2 . 构造出一个 UDP 请求,发送给服务器DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(this.serverIP),this.serverPort);socket.send(requestPacket);// 3 . 从服务器读取响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response = new String (responsePacket.getData(),0,responsePacket.getLength());// 4 . 将响应打印到控制台上System.out.println("response");}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);client.start();}
}

三 . 基于 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;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){// accept 相当于针对内核中已经建立好的连接进行 “确认” 的动作Socket clientSocket = serverSocket.accept();//使用多线程解决同一个服务器服务于多个客户端//创建线程,每个线程服务于一个客户端Thread t = new Thread(() ->{try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();//使用线程池service.submit(() -> {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});}}//针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) throws IOException {//先打印一下客户端的信息System.out.printf("[%s:%d] 客户端上线!!!\n",clientSocket.getInetAddress(),clientSocket.getPort());//获取到 socket 中持有的流对象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//使用 Scanner 包装一下 inputStream ,就可以更方便的读取这里的请求数据了Scanner scan = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while(true){// 1 . 读取请求并解析if(!scan.hasNext()){//如果 scan 中无法读取数据,则说明客户端关闭了连接//导致服务器这边读取到 “末尾”break ;}String request = scan.next();// 2 . 根据请求计算响应String response = process(request);// 3 . 将响应写回客户端//此处可以按照字节数组直接来写,也可以有另一种写法
//                outputStream.write(response.getBytes());printWriter.println(response);printWriter.flush();//此处用 prrintln 的原因是://读取数据的时候,隐藏了条件,请求应该是以 ”空白符“ 结尾的//包括但不限于:空格、回车、制表符、垂直制表符、翻页付//所以此处就约定,使用 “\n” 来作为请求和响应的结尾// 4 . 打印日志System.out.printf("[%s:%d] rep = %s ; resp = %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch(IOException e){e.printStackTrace();}finally {System.out.printf("[%s:%d] 客户端下线!!!\n",clientSocket.getInetAddress(),clientSocket.getPort());clientSocket.close();}}//请求什么,则返回什么响应private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}}
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 TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP,int serverPort) throws IOException {socket = new Socket(serverIP,serverPort);}public void start(){System.out.println("客户端启动!!!");try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){Scanner scanner = new Scanner(inputStream);Scanner scannerIn = new Scanner(System.in);PrintWriter printWriter = new PrintWriter(outputStream);printWriter.flush();//Ctrl + Alt + Twhile(true){// 1 . 从控制台读取数据System.out.println(" -> ");String request = scannerIn.next();// 2 . 把请求发送给服务器printWriter.println(request);//刷新缓冲区printWriter.flush();// 3 . 从服务器读取响应if(!scanner.hasNext()){break;}String response = scanner.next();// 4 . 打印响应结果System.out.println(response);}}catch(Exception e){throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);client.start();}

OKK,今天就说到这里吧,本期主要介绍了通过 TCP 和 UDP 实现回显服务器和客户端。代码部分比较生硬,因为都是一些陌生的 API ,这一部分只有理解加上熟能生巧了。咱们下期再见吧,与诸君共勉!!!

相关文章:

  • JUnit​​ 和 ​​Mockito​​ 的详细说明及示例,涵盖核心概念、常用注解、测试场景和实战案例。
  • nprogress效果和网页进度不一致问题
  • 在Window上安装和配置VTK9.x,并在QT项目中调试VTK是否可用
  • Lrc歌词分析
  • 简单了解一下Hugging Face(抱抱脸)
  • C++中的右值引用与移动语义的理解
  • @Transactional注解失效的原因有哪些?
  • 如何对Video视频进行SEO优化?
  • OLED(SSD306)移植全解-基于IIC
  • Semaphore - 信号量
  • CPP基础
  • 西门子 S7-1200 PLC 海外远程运维技术方案
  • DAX权威指南8:DAX引擎与存储优化
  • 第七章:未名湖畔的樱花网关
  • 书籍推荐 --- 《筚路维艰:中国经济社会主义路径的五次选择》
  • 【信息系统项目管理师-案例真题】2025上半年(第二批)案例分析答案和详解(回忆版)
  • ​​Java 异常处理​​ 的详细说明及示例,涵盖 try-catch-finally、自定义异常、throws 与 throw 的核心概念和使用场景
  • 在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
  • Benchmarking Potential Based Rewards for Learning Humanoid Locomotion
  • 关于锁策略的简单介绍
  • 我要浏览国外网站怎么做/链接是什么意思
  • 电子商务网站建设报价表/百度指数如何提升
  • 官方网站如何建立/站长工具网址查询
  • mibt wordpress/滕州seo
  • 安庆网站建设服务网/高权重外链
  • 假冒建设厅网站/优化营商环境工作总结