网络编程---Socket

news2025/2/23 4:41:43

文章目录

  • 网络编程基础
    • 什么是网络编程?
    • 网络编程的基本概念
  • 网络编程实现
    • Socket套接字
    • UDP网络通信流程(回显服务器)
      • 服务器:
      • 客户端
      • 完整代码示例:
    • TCP网络通信流程(回显服务器)
      • 服务器
      • 客户端
      • 完整的代码示例:
    • 两种实现方式的区别

网络编程基础

什么是网络编程?

在日常生活中,我们可以通过浏览器来查看文档资料、看视频、听音乐,这些都是获取网络资源的方式。而这些网络上的资源是如何显示在我们电脑上的呢?这就是网络编程。即:网络资源通过网络编程来进行数据传输。

官方一点的定义,网络编程是指网络上的主机通过不同的进程,以编程的方式实现网络通信。

当然,即使在一个主机中,只要是不同的进程基于网络来传输数据也属于网络编程。

网络编程的基本概念

发送端:数据的发送方进程,即网络通信中的源主机
接收端:数据的接收方进程,即网络通信中的目的主机
收发端:发送端和接收端两端。

请求:请求数据的发送。
响应:响应数据的发送。

客户端:获取服务的一方进程。
服务器:提供服务的一方进程。

上面提到的发送端和接收端其实是相对的:
在这里插入图片描述

网络编程实现

Socket套接字

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

网络通信显然是发生在传输层的过程,而传输层最出名的便是TCP协议和UDP协议了。由于TCP传输和UDP传输的特点不同。因此,Socket也被分为了俩类:TCP的Socket和UDP的Socket。下面我们分别进行介绍:
在这里插入图片描述

UDP网络通信流程(回显服务器)

在这里插入图片描述
在这里插入图片描述

服务器:

  1. 创建Socket 绑定端口号
	//创建一个socket
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
  1. 启动服务器
	//启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            

        }
    }

因为不知道客户端什么时候发送请求,所以使用while(true) 一直保持等待

  1. 创建Packet来接收客户端传来的数据
	//接受客户端传来的数据
    DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
    socket.receive(requestPacket);
    String request = new String(requestPacket.getData(),0,requestPacket.getLength());

转化为字符串更方便使用。 接收数据的字节数组可以根据实际情况选择合适大小

  1. 根据请求计算响应
    //根据请求计算响应
    String response = process(request);

	//因为是回显服务器  就是返回和请求相同的数据  所以不用处理
	public String process(String request){
        return request;
    }
  1. 把响应返回给客户端
    //把响应返回给客户端
    DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
    socket.send(responsePacket);

客户端

  1. 创建Sockket 确定服务器的IP和端口号
	//用于网络编程 发送数据
    private DatagramSocket socket = null;
    //指定发送数据到哪儿
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //先new出socket   不需要指定发送的端口   客户端会自动分配一个空闲端口
        socket = new DatagramSocket();

        //构造发送去哪儿
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
  1. 启动客户端
	public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true){
            

        }
    }

也需要使用while(true)来时刻准备接收返回的数据

  1. 发送数据
     //1.从控制台读取要发送的数据
     System.out.println("请输入要发送的数据");
     String request = scanner.next();
     //2.构造packet
     DatagramPacket requestPacker = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(this.serverIP),this.serverPort);
     //3.发送给服务器
     socket.send(requestPacker);
  1. 接收数据
    //1.从服务器读取响应数据
    DatagramPacket responsePacker = new DatagramPacket(new byte[4096],4096);
    socket.receive(responsePacker);
    String response = new String(responsePacker.getData(), 0,responsePacker.getLength());

    //2.把服务器响应显示在控制台
    System.out.println(response);

完整代码示例:

//服务器
public class UdpEchoServer {
    //创建一个socket
    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);
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

            //2.根据请求计算响应
            String response = process(request);

            //3.把响应返回给客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            //4.打印一个日志记录当前情况
            System.out.printf("%s - %d  request: %s  response: %s \n",requestPacket.getAddress().toString(),requestPacket.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();
    }
}

//客户端
public class UdpEchoClient {
    //用于网络编程 发送数据
    private DatagramSocket socket = null;
    //指定发送数据到哪儿
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //先new出socket   不需要指定发送的端口   客户端会自动分配一个空闲端口
        socket = new DatagramSocket();

        //构造发送去哪儿
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }


    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true){
            //1.从控制台读取要发送的数据
            System.out.println("请输入要发送的数据");
            String request = scanner.next();
            //2.构造packet
            DatagramPacket requestPacker = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(this.serverIP),this.serverPort);
            //3.发送给服务器
            socket.send(requestPacker);

            //4.从服务器读取响应数据
            DatagramPacket responsePacker = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacker);
            String response = new String(responsePacker.getData(), 0,responsePacker.getLength());

            //5.把服务器响应显示在控制台
            System.out.println(response);

        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start();
    }
}

TCP网络通信流程(回显服务器)

在这里插入图片描述
在这里插入图片描述

服务器

  1. 创建ServerSocket用来监听
	//创建一个 监听socket 用于客户端和服务器的连接
    private ServerSocket listenSocket = null;
    public TcpEchoServer(int port) throws IOException {
        listenSocket = new ServerSocket(port);
    }
  1. 启动服务器
	//启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        
        while (true){
            
        }
    }
  1. 建立连接
Socket clientSocket = listenSocket.accept();
  1. 处理连接
processConnection(clientSocket);

private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("已经和客户端建立连接 ip:%s  port: %d \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());

        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()){
            while (true){
                //1.接受请求并解析
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()){
                    //接受完毕  断开连接
                    System.out.printf("断开连接 ip: %s  port: %d",clientSocket.getInetAddress().toString(),clientSocket.getPort());

                    break;
                }
                String request = scanner.next();

                //2.根据请求计算响应
                String response = process(request);

                //3.向客户端返回响应
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();

                //处理一次请求的详情
                System.out.printf("ip: %s  port: %d  request: %s  response: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }


    }
  1. 如果有多个客户端要建立连接,使用线程池加快处理的速度
	public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService pool = Executors.newCachedThreadPool();
        while (true){
            //1.调用listenSocket来建立连接
            Socket clientSocket = listenSocket.accept();
            //2.处理连接  如果有多个客户端发起请求  这样的响应速度就会很慢
            
            //使用线程池

            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

客户端

  1. 尝试建立连接
	//创建一个socket来传输数据
    private Socket socket = null;

    //tcp需要先建立连接再传输数据   就需要先在socket里指定出服务器
    public TcpEchoClient(String serverIP,int serverPort) throws IOException {
        socket = new Socket(serverIP,serverPort);
    }
  1. 启动服务器
	public void start() throws IOException {
        
    }

while(true)放到了传输数据的模块

  1. 发送请求并接收响应
		Scanner scanner = new Scanner(System.in);

        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){
            while (true){
                //1.从控制台读取数据  构造请求
                System.out.println("请输入要传输的数据");
                String request = scanner.next();
                //2.发送请求给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4.把响应显示在客户端
                System.out.println(response);
            }
        }

完整的代码示例:

//服务器
public class TcpEchoServer {
    //创建一个 监听socket 用于客户端和服务器的连接
    private ServerSocket listenSocket = null;
    public TcpEchoServer(int port) throws IOException {
        listenSocket = new ServerSocket(port);
    }

    //启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService pool = Executors.newCachedThreadPool();
        while (true){
            //1.调用listenSocket来建立连接
            Socket clientSocket = listenSocket.accept();
            //2.处理连接  如果有多个客户端发起请求  这样的响应速度就会很慢
           
            //使用线程池

            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("已经和客户端建立连接 ip:%s  port: %d \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());

        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()){
            while (true){
                //1.接受请求并解析
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()){
                    //接受完毕  断开连接
                    System.out.printf("断开连接 ip: %s  port: %d",clientSocket.getInetAddress().toString(),clientSocket.getPort());

                    break;
                }
                String request = scanner.next();

                //2.根据请求计算响应
                String response = process(request);

                //3.向客户端返回响应
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();

                //处理一次请求的详情
                System.out.printf("ip: %s  port: %d  request: %s  response: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }


    }

    //处理响应
    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}
//客户端
public class TcpEchoClient {
    //创建一个socket来传输数据
    private Socket socket = null;

    //tcp需要先建立连接再传输数据   就需要先在socket里指定出服务器
    public TcpEchoClient(String serverIP,int serverPort) throws IOException {
        socket = new Socket(serverIP,serverPort);
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);

        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){
            while (true){
                //1.从控制台读取数据  构造请求
                System.out.println("请输入要传输的数据");
                String request = scanner.next();
                //2.发送请求给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4.把响应显示在客户端
                System.out.println(response);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();
    }

}

两种实现方式的区别

为什么实现TCP的Socket时要使用多线程而在UDP的Socket不用呢?

  1. UDP是无连接的,服务器不关心是哪个客户端发送过来的请求都会进行处理。
  2. TCP是有连接的,每次建立连接后服务器都得处理对应客户端的多次请求,此时无法接受其他客户端的连接,不满足实际需求。所以得使用多线程来同时处理多个客户端的请求。

为什么TCP的Socket要关闭,ServerSocket和UDP的Socket不用关闭?

  1. Socket也是一个文件,一个进程能同时打开文件的个数是有限的。因为文件描述符表是有限的。
  2. UDP的Socket不需要建立连接,它在UDP服务器中只有唯一一个对象,不会把文件描述符表占满,随着进程退出会自动释放。
  3. ServerSocket,它在TCP服务器中只有唯一一个对象,不会把文件描述符表占满,随着进程退出会自动释放。
  4. Socket,在TCP的服务器中,每次和一个新的客户端建立连接都要创建一个新的。有可能把文件描述符表占满,就需要释放。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1161321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医保经办系统练兵比武竞赛中用到的软件和硬件

全国医保经办系统练兵比武竞赛包括必答题、抢答题、案例题、实操题、风险题,用到选手端平板、评委端平板、主持人平板、抢答器等设备。分别计算团队分和个人分。答题规则和计分方案均较为复杂,一般竞赛软件无法实现,要用到高端竞赛软件&#…

谷歌动态搜索广告被滥用引发恶意软件泛滥

研究人员发现了一种新方法,可以利用易受攻击的网站向搜索引擎用户发送恶意的、有针对性的广告,这种方法能够传播大量恶意软件,使受害者完全不知所措。 关键是“动态搜索广告”,谷歌利用网站登陆页面的内容将目标广告与搜索配对的…

Kubernetes 高级调度 - Affinity

Author:rab 目录 前言一、Node 亲和性1.1 NodeAffinity1.1.1 Hard Node Affinity1.1.2 Soft Node Affinity 1.2 NodeAntiAffinity 二、Pod 亲和性2.1 PodAffinity2.1.1 Hard Pod Affinity2.1.2 Soft Pod Affinity 2.2 PodAntiAffinity 总结 前言 Kubernetes 中的 A…

OceanBase:02-单机部署(生产环境)

目录 一、部署规划 二、配置要求 三、部署前配置 1.配置 limits.conf 2.配置 sysctl.conf 3.关闭防火墙 4.关闭 SELinux 5.创建数据目录,修改文件所有者信息 6.设置无密码 SSH 登录 7.安装jdk 四、解压执行安装 五、OBD命令行部署 1.修改配置文件(all-c…

网络质量探测

目录 一.BFD监测网络状态 二. NQA检测网络状态 一.BFD监测网络状态 BFD(BidrectionaL Forwarding Detection 双向转发检测)用于快速检测系统设备之间的发送和接受两个方向的通信故障,并在出现故障时通知生成应用。BFD 广泛用于链路故障检测,并能实现与…

探究Java虚拟机运行时数据区,了解方法区的奥秘

目录 一、栈、堆、方法区交互关系 二、方法区的理解 三、HotSpot中方法区的演进 四、设置方法区大小与OOM 五、如何解决OOM 六、方法区的内部结构 (一)类型变量 (二)域信息 (三)方法信息 &#x…

AN动画基础——遮罩动画

【AN动画基础——遮罩动画】 什么是遮罩动画基本使用方法实战:水墨遮罩 本篇内容:了解遮罩动画 重点内容:遮罩动画应用 工 具:Adobe Animate 2022 什么是遮罩动画 遮罩动画是一种常见的图形效果,利用遮罩层来实现元素…

ARPG----C++学习记录02 Section6位置,偏移,函数

设置actor位置 这一句代码就可以更改位置和旋转 给位置添加偏移offset 将debug的持久都设置为false,在tick中调用,球就会动。这是每帧移动,所以移动速度和帧率有关,需要更改 void Aitem::Tick(float DeltaTime) {Super::Tick(DeltaTime);Ad…

【洛谷算法题】P5710-数的性质【入门2分支结构】

👨‍💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5710-数的性质【入门2分支结构】🌏题目描述🌏输入格式&a…

Nginx搭配负载均衡和动静分离:构建高性能Web应用的完美组合

前言 在当今互联网时代,高并发访问已成为Web应用程序面临的重要挑战之一。为了保证系统的稳定性和用户体验,我们需要采取有效的措施来应对这一挑战。本文将介绍如何利用Nginx搭配负载均衡和动静分离技术,构建高性能的Web应用。 一、Nginx简…

java反射API

反射 什么是Java的动态机制什么是反射机制什么是Class类Class提供了诸多的get方法 反射机制实例化对象Class提供了一个方法 Constructor类指定构造器实例化对象 Method类获取一个类中的所有方法Class类提供了对应的方法 获取本类自定义的所有方法Class类提供了对应的方法 获取表…

第四章 应用SysML基本特性集的汽车示例 P2(断更)|系统建模语言SysML实用指南学习

仅供个人学习 使用试用版CSM很鸡肋,然后书中一些内容没有说明,自定义方面有点困难,第四章暂时停止 同时感觉画图的顺序也很随意?甚至需求图放在了后面,觉得很离谱。 准备跳过这一章节 汽车模型 续P1 序列图表示启…

SolidWorks2019安装教程(正版)

网盘资源附文末 一.简介 SolidWorks软件是世界上第一个基于Windows开发的三维CAD系统,由于技术创新符合CAD技术的发展潮流和趋势,SolidWorks公司于两年间成为CAD/CAM产业中获利最高的公司。良好的财务状况和用户支持使得SolidWorks每年都有数十乃至数百…

Es中出现unassigned shards问题解决

1、一般后台会报primary shard is not active Timeout: … 出现这种问题表示该索引是只读了,没办法进行shard及存储操作,优先排除是系统存储盘满了 2、通过监控工具查看(cerebro) 发现该索引shard 1 损坏 也可以通过命令进行查看 GET _cluster/allo…

阿里云短信服务接口返回: 只能向已回复授权信息的手机号发送

1、问题描述 在阿里云短信服务控制台,调用发送短信接口,报错:只能向已回复授权信息的手机号发送 2、问题分析 所使用的签名 是 测试or个人学习的 ,所以会导致 有的手机号发送不出去验证码 3、解决 如果在测试阶段,非要…

Python小试牛刀:GUI(图形界面)实现计算器UI界面(一)

下一篇:Python小试牛刀:GUI(图形界面)实现计算器UI界面(二)-CSDN博客 Python GUI 是指 Python 图形用户界面库,它们可以帮助开发者创建在计算机上运行的图形用户界面(GUI)。下面是一些常用的 P…

64T存储松下mov和索尼mp4文件变0字节恢复案例

64T存储松下mov和索尼mp4文件变0字节恢复案例 小型入门的小NAS凭借超市的性价比在各行业中开始流行,可以通过搭配普通SATA硬盘就可以完成阵列上线,部署也很简单,一根网线就搞定。我们看一个影视公司64T小NAS存储比较奇怪的恢复案例。 故障存…

【JavaScript保姆级教程】for循环与for循环遍历数组

文章目录 前言一、for循环1.1 什么是For循环?1.2 For循环示例1. 打印偶数2. 倒序打印 1.3 For循环遍历数组 二、JavaScript中的循环嵌套1.1 while循环的嵌套示例1:乘法表 示例2:打印星号三角形2.2 for循环的嵌套示例1:二维数组遍历…

pwd - 显示当前工作目录的路径

pwd命令来自英文词组“print working directory”的缩写,其功能是用于显示当前工作目录的路径,即显示所在位置的绝对路径。 在实际工作中,我们经常会在不同目录之间进行切换,为了防止“迷路”,可以使用pwd命令快速查看…

linux二进制文件分析三大工具详解(ldd、readelf、nm)

介绍 测试代码源码、源码如下&#xff1a; #include <openssl/evp.h> #include <openssl/sm2.h> #include <openssl/rand.h> #include <iostream> #include <string>EVP_PKEY* generate_sm2_key_pair() {EVP_PKEY_CTX *ctx;EVP_PKEY *pkey nu…