Socket(四)

news2024/11/25 18:54:52

文章目录

    • 1. 服务器Socket简介
    • 2. 使用ServerSocket
    • 3. 用Socket写入服务器
    • 4. 关闭服务器Socket

1. 服务器Socket简介

博客Socket(一)~Socket(二)从客户端的角度讨论了Socket,客户端就是向监听连接的服务器打开一个Socket程序。不过只有客户端Socket还不够,如果不能和服务器通信,客户端没有什么用处,所以服务器也要创建Socket,编写服务器时,无法预先了解哪个主机会联系你,即使确实知道,你也不清楚哪个主机希望和你连接。换句话说,服务器就像坐在电话旁等电话一样,不知道谁会打来电话,或者什么时间打电话,所以只用Socket类是不能做到这一点的。对于接受连接的服务器,Java提供了一个ServerSocekt类表示服务器Socket。服务器Socket在服务器上运行 ,监听入站TCP连接。每个服务器Socket监听服务器上的一个特定端口,当远程主机上的一个客户端尝试连接这个端口时,服务器就会被唤醒,协商建立客户端和服务器之间的连接,并返回一个常规的Socket对象,表示两个主机之间的Socket。换句话说,服务器Socket等待连接,而客户端Socket发起连接,一旦ServerSocket建立了连接,服务器会使用一个常规的Socket对象向客户端发送数据,数据总是通过常规的Socket传送。

2. 使用ServerSocket

ServerSocket类包含了使用Java编写服务器所需的全部内容,其中包括创建新ServerScoket对象的构造函数、在指定端口监听连接的方法、配置各个服务器Socket选项的方法,以及其他一些常见的方法,如tostring()。在java中程序的生命周期如下:

  1. 使用一个ServerSocket()构造函数在一个特定的端口创建一个新的ServerSocket
  2. ServerSocket使用其accept()方法监听这个端口的入站连接。accept()会一直阻塞,直到一个客户端尝试建立连接,此时accept()返回一个连接客户端和服务器的Socket对象
  3. 根据服务器的类型,会调用Socket的getInputStream()方法或getOutputStream()方法,或者这两个方法都使用,以获取与客户端通信的输入和输出流
  4. 服务器和客户端根据已协商的协议交互,直到要关闭连接
  5. 服务器或客户端(或二者)关闭连接
  6. 服务器返回步骤2,等待下一次连接

下面代码是daytime服务器的一个简单本地实现,首先是服务端代码

class myserver extends Thread{
    @Override
    public void run(){
        try {
            System.out.println("服务器端开始运行:"+System.currentTimeMillis());
            ServerSocket serverSocket=new ServerSocket(8080);
            Socket connection=serverSocket.accept();
            OutputStream outputStream=connection.getOutputStream();
            Writer writer=new OutputStreamWriter(outputStream);
            Date now=new Date();
            writer.write(now.toString()+" 你龙哥服务器 "+"\r\n");
            writer.flush();
            connection.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

我们首先让服务端运行,然后用telnet测试一下

在这里插入图片描述

然后是客户端代码

class User extends Thread{
    @Override
    public void run(){
        try {
            System.out.println("客户端开始运行:"+System.currentTimeMillis());
            Socket socket=new Socket();
            SocketAddress socketAddress=new InetSocketAddress("127.0.0.1",8080);
            socket.connect(socketAddress);
            InputStream inputStream=socket.getInputStream();
            InputStreamReader reader=new InputStreamReader(inputStream);
            int c;
            StringBuilder mystring=new StringBuilder();
            while((c=reader.read())!=-1){
                mystring.append((char)c);
            }
            System.out.println(mystring);
            inputStream.close();
            socket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } 
    }
}

最后是main函数代码

  public static void main(String[] args) throws InterruptedException {
       myserver server=new myserver();
       server.start();
       Thread.sleep(3000);
       User user=new User();
       user.start();
    }

查看运行结果

在这里插入图片描述

3. 用Socket写入服务器

在目前为止所有的例子中,服务器只写入客户端Socket,而没有从客户端读取,不过,大多数协议要求服务器同时读写客户端。这并不难,与从前一样,仍然要接受一个连接,不过这一次要同时获取一个InputStream和一个OutputStreamn。使用InputStream读取客户端,另外使用OutputStream写入客户端,关键是理解协议:明确何时写入和何时读取。RFC 862定义的echo协议是最简单的交互式TCP服务器,客户端打开指向echo服务器端口7的Socket,并发送数据,服务器将数据发回。这个过程一直继续,知道客户端关闭连接为止。 与Telnet的从控制体接受输入,把它发送给服务器,然后等待服务器读取返回一行输出不同,echo它会回显收到的每一个字节,它并不关心这些字节是否表示采用某种编码的字符,或者是划分为多行,与很多协议不同,echo没有指定锁步行为,即客户端发送一个请求,然后在发送更多数据之前会等待完整的服务器响应。echo协议需要客户端自己关闭连接。

下面是echo服务器的简单实现

public static void main(String[] args) throws InterruptedException {
        int port;
        try{
            port=8080;
        }catch (RuntimeException e){
            port=7;
        }
        System.out.println("Listening for connections on port  "+port);
        ServerSocketChannel serverSocketChannel;
        Selector selector;
        try{
            serverSocketChannel =ServerSocketChannel.open();
            ServerSocket ss=serverSocketChannel.socket();
            InetSocketAddress address=new InetSocketAddress(port);
            ss.bind(address);
            serverSocketChannel.configureBlocking(false);
            selector=Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
            return;
        }
        while (true){
            try{
                selector.select();
            }catch (IOException e){
                e.printStackTrace();
                break;
            }
        }
        Set<SelectionKey> readyKeys=selector.selectedKeys();
        Iterator<SelectionKey> iterator=readyKeys.iterator();
        while(iterator.hasNext()){
            SelectionKey key=iterator.next();
            iterator.remove();
            try{
                if(key.isAcceptable()){
                    ServerSocketChannel server=(ServerSocketChannel) key.channel();
                    SocketChannel client=server.accept();
                    System.out.println("Accepted connection from:"+client);
                    client.configureBlocking(false);
                    SelectionKey clientkey=client.register(selector,SelectionKey.OP_WRITE|SelectionKey.OP_READ);
                    ByteBuffer buffer=ByteBuffer.allocate(100);
                    clientkey.attach(buffer);
                }
                if(key.isAcceptable()){
                    SocketChannel client=(SocketChannel) key.channel();
                    ByteBuffer output=(ByteBuffer) key.attachment();
                    client.read(output);
                }
                if(key.isWritable()){
                    SocketChannel client=(SocketChannel) key.channel();
                    ByteBuffer output=(ByteBuffer) key.attachment();
                    output.flip();
                    client.write(output);
                    output.compact();
                }
            } catch (ClosedChannelException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                key.cancel();
                try {
                    key.channel().close();
                }catch (IOException ex){}
            }
        }
    }

当你运行这段代码时,它将创建一个简单的服务器端,用于监听指定的端口(默认为8080)上的连接。服务器使用非阻塞模式进行处理。

  • 首先,它尝试将端口设置为8080,如果出现RuntimeException异常,则将端口设置为7。
  • 然后,它打印一条消息,表示服务器正在监听指定端口上的连接。
  • 接下来,它创建一个ServerSocketChannel对象和一个Selector对象。它打开ServerSocketChannel并获取关联的ServerSocket对象。然后,它将ServerSocket绑定到指定的端口,并将ServerSocketChannel设置为非阻塞模式。最后,它将ServerSocketChannel注册到Selector上,关注连接请求事件。
  • 然后,代码进入一个无限循环,不断调用selector.select()方法来检查是否有就绪的事件发生。如果发生了IOException异常,循环会中断。
  • 在循环中,它获取就绪的SelectionKey集合,并遍历每个SelectionKey。根据SelectionKey的不同状态,它执行相应的操作:
  • 如果SelectionKey是可接受的(isAcceptable),表示有新的连接请求,它会接受连接,打印连接信息,并将新连接的SocketChannel注册到Selector上,关注读写事件。
  • 如果SelectionKey是可读的(isReadable),表示有数据可读取,它会读取数据到ByteBuffer中。
  • 如果SelectionKey是可写的(isWritable),表示可以写入数据,它会从ByteBuffer中取出数据并写入到SocketChannel中。
  • 在处理每个SelectionKey时,如果发生了ClosedChannelException异常,它会抛出RuntimeException异常。如果发生了IOException异常,它会取消该SelectionKey并关闭相应的通道。
  • 这个循环将一直执行,直到程序被终止。这样,服务器就可以接受并处理来自客户端的连接和数据。

注意:上面的代码涉及JavaNIO的知识

4. 关闭服务器Socket

如果使用完一个服务器Socket,就应当把它关闭,这个关闭Socket是不一样的,关闭ServerSocket会释放本地主机的一个端口,运行另一个服务器绑定到这个端口。他还会中断该ServerSocket已经接受的目前处于打开状态的所有Socket。程序结束时,服务器Socket会自动关闭,但手动关闭服务也是没有什么坏处,如下:

ServerSocket server=null;
try{
   server=new ServerSocket(port);
 }finally{
 if(server!=null)
 {
    try{
       server.close();
      }catch(IOException ex){
      }
 }
}

可以使用无参数的ServerSocket()构造函数稍加改进,无参数构造函数不抛出任何异常,也不绑定到一个端口,你需要之后调用bind()方法来绑定一个Socket地址

ServerSocket server=new ServerSocket();
try{
   SocketAddress address=new InetSocketAddress(port);
   server.bind(address);
 }finally{
 if(server!=null)
 {
    try{
       server.close();
      }catch(IOException ex){
      }
 }
}

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

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

相关文章

Git基本概念

Git基础 git 介绍git工作流程 git 介绍 1、git是什么&#xff1a;是目前世界上最先进的分布式版本控制系统 2、git的优势 ● 适合分布式开发、强调个体。 ● 公共服务器压力和数量都不会太大 ● 速度快、灵活 ● 任意两个开发者之间可以很容易的解决冲突 ● 离线工作 3、git能…

华为OD机试真题B卷 Java 实现【乱序整数序列两数之和绝对值最小】,附详细解题思路

一、题目描述 给定一个随机的整数&#xff08;可能存在正整数和负整数&#xff09;数组 nums&#xff0c;请你在该数组中找出两个数&#xff0c;其和的绝对值(|nums[x]nums[y]|)为最小值&#xff0c;并返回这个两个数&#xff08;按从小到大返回&#xff09;以及绝对值。 每种…

全志V3S嵌入式驱动开发(网卡驱动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 一开始上来就说网卡&#xff0c;好像有点不太对劲。这主要是因为如果开发板可以连接网络的话&#xff0c;可以帮我们节约很多的时间&#xff0c;开…

逍遥自在学C语言 | 揭开while循环的神秘面纱

前言 循环是一种重要的控制结构&#xff0c;可以使程序重复执行一段代码&#xff0c;直到满足特定条件为止。 在C语言中&#xff0c;while和do-while是两种常用的循环结构&#xff0c;本文将详细介绍这两种循环的用法。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一…

eclipse安装lombok插件

项目中遇到了一些实体类声明了属性&#xff0c;缺少get/set方法&#xff0c;但是类上使用了Getter 和 Setter注解&#xff0c;查了下是lombok插件的强大功能。 这里先不看lombok的功能&#xff0c;先看下eclipse安装lombok插件的过程。 1、 https://projectlombok.org/downlo…

三十五、数学知识——快速幂(反复平方法 + 快速幂求逆元)

快速幂算法主要内容 一、基本原理1、概念 暴力求解2、核心原理——反复平方法3、快速幂求逆元 二、Java、C语言模板实现三、例题题解 一、基本原理 1、概念 暴力求解 问题目标&#xff1a; 快速求出 a^k mod p 的结果&#xff0c;时间复杂度为 O(logk)&#xff0c;其中 a,p…

华为OD机试真题B卷 Java 实现【字符串变换最小字符串】,附详细解题思路

一、题目描述 给定一个字符串s&#xff0c;最多只能进行一次变换&#xff0c;返回变换后能得到的最小字符串&#xff08;按照字典序进行比较&#xff09;。 变换规则&#xff1a;交换字符串中任意两个不同位置的字符。 二、输入描述 一串小写字母组成的字符串s。 三、输出…

人工智能-不确定性的量化

这一部分的重点内容有&#xff1a; 贝叶斯规则贝叶斯网络基于隐马尔可夫模型的概率推理 大概会有以下几种考察形式&#xff0c;有答案的历年期末考试题只解释一些注意的地方 贝叶斯网络-独立性 第二问答案很详细&#xff0c;第一问看↓ D-separation&#xff1a; 判断贝叶…

Stable-diffusion-webui 本地部署及使用

Stable-diffusion-webui 本地部署及使用 本地部署stable-diffusion-webui(windows)1.安装conda&#xff1b;2.创建conda env 和python 3.10.6&#xff1b;3.更新pip&#xff1b;4.安装cuda 11.8&#xff1b;5.克隆stable-diffusion-webui仓库 &#xff1b;6.下载SD模型7.运行sd…

Dubbo配置

dubbo配置官网参考 1.配置原则 JVM 启动 -D 参数优先&#xff0c;这样可以使用户在部署和启动时进行参数重写&#xff0c;比如在启动时需改变协议的端口。 XML 次之&#xff0c;如果在 XML 中有配置&#xff0c;则 dubbo.properties 中的相应配置项无效。 Properties 最后&a…

【群智能算法改进】一种改进的蝴蝶优化算法 改进蝴蝶优化算法 改进BOA[1]【Matlab代码#35】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始蝴蝶优化算法2. 改进蝴蝶优化算法2.1 动态转换概率策略2.2 最优邻域扰动策略2.3 随机惯性权重策略 3. 部分代码展示4. 仿真结果展示5. 资源获取6. 参考文献 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原…

React面试题和基础

React的特点&#xff1a; JSX它使用虚拟DOM &#xff0c;减少 DOM 操作&#xff0c;提升性能。便于和其他平台集成。它可以进行服务器端渲染。单向数据流。组件化 双向数据绑定和单向数据流区别&#xff1f; 单向绑定的优点在于清晰可控&#xff0c;缺点则在于模板代码过多。…

第一部分-基础篇-第二章:PSTN、PBX及呼叫中心业务

文章目录 序言上一篇文章&#xff1a;2.1 PSTN业务2.1.1 POTS2.1.2 商务业务2.1.3 其他增值业务 2.2 PBX业务2.2.1 呼叫转移2.2.2 同组代答 2.3 PBX与中继线2.4 IP-PBX业务2.5 呼叫中心2.5.1 什么是呼叫中心2.5.2 呼叫中心的历史2.5.3 呼叫中心的分类1.交换机类型的呼叫中心2.板…

C++——string

作者&#xff1a;几冬雪来 时间&#xff1a;2023年5月29日 内容&#xff1a;C——string内容讲解 目录 前言&#xff1a; 1.string&#xff08;续&#xff09;&#xff1a; 1.string::operator[]: 2.string:: length()&#xff1a; 3.string:: max_size(): 4.strin…

华南农业大学|图像处理与分析技术综合测试|题目解答:识别时钟上时间

设计任务 这是一幅表盘图像&#xff0c;试采用图像处理和分析技术&#xff0c;设计适当的算法和程序&#xff0c;计算出表盘上的指示时间&#xff08;要求精确到秒&#xff09;。请按统一要求写出算法原理、设计流程&#xff0c;并完成测试分析等报告内容。 算法设计 解题思路…

张小飞的Java之路——第四十二章——字节流

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 诸小亮&#xff1a;下面我们学习——字节流 张小飞&#xff1a;什么是字…

article-并联机械手爪运动学分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aNKIR4E-1685371700448)(data:image/svgxml;utf8, )] 2.4.3 基于Robotics Toolbox的工具箱的模型检测 上文中&#xff0c;我们已经对采摘机器手爪运动学理论模型进行了创建&#xff0c;接下来要用MA…

V3.0_用exec族函数替代system()

注意点&#xff1a;exec族函数的使用 以execl为例&#xff1a; &#xff08;1&#xff09; &#xff08;2&#xff09;exec族函数中的函数调用失败时会设置error并返回-1&#xff0c;然后从源程序调用点接着往下执行。 执行成功后不会返回&#xff0c;也不会从源程序调用点接…

Redis缓存一致性问题及解决方案

对于没有并发的用户请求 先更新缓存&#xff0c;后更新数据库先更新数据库&#xff0c;后更新缓存 两者第二步没成功&#xff0c;都有问题 如果更新缓存成功&#xff0c;更新数据库没成功&#xff0c;一旦缓存失效&#xff0c;读取的仍是旧值如果更新数据库成功&#xff0c;…

MySQL官网下载Linux版本安装包

步骤一&#xff1a;https://www.mysql.com/ 步骤二&#xff1a;https://www.mysql.com/downloads/ 步骤三&#xff1a;https://dev.mysql.com/downloads/ 步骤四&#xff1a;https://dev.mysql.com/downloads/mysql/