Socket(四)

news/2025/1/15 13:49:03/

文章目录

    • 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{@Overridepublic 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{@Overridepublic 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.ppmy.cn/news/104254.html

相关文章

AJAX(一) -- AJAX的快速入门

目录 1. AJAX的概述: 2. JQuery的通用方式实现AJAX 3. JQuery的GET方式实现AJAX 4. JQuery的POST方式实现AJAX

通俗易懂理解到底什么是DevOps技术(Windows程序员视角)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来聊聊到底什么是DevOps。 相信很多小伙伴为什么搞懂DevOps&#xff0c;已经不知道查了多少论坛的帖子和资料了&#xff0c;但还是很困惑的话&#xff0c;那不妨来看看我这个帖子。希望能有助于你的…

带你开发一个远程控制项目---->STM32+标准库+阿里云平台+传感器模块+远程显示-------之 阿里云平台项目建造。

第一篇章&#xff1a; (13条消息) 带你开发一个远程控制项目----&#xff1e;STM32标准库阿里云平台传感器模块远程显示。_海口飞鹏岛科技有限公司的博客-CSDN博客 本次文章是指引开发者进行开发阿里云平台建造设备项目&#xff0c;可观看UP主教程&#xff0c;完成如下&#x…

MapperFacade使用

一、MapperFacade是Orika框架中的一个核心类&#xff0c;它用于管理对象映射。使用MapperFacade可以方便地将一个对象转换为另一个对象。以下是使用MapperFacade的基本步骤&#xff1a; 创建一个MapperFactory对象。 使用MapperFactory对象注册对象之间的映射关系。 调用Mapp…

Presto从入门到精通以及案例实操系列

1、简介 1.1、Presto的由来 Presto最初由Facebook公司开发&#xff0c;旨在解决Facebook内部大规模数据处理和数据分析的问题。在传统的Hadoop生态圈中&#xff0c;MapReduce作为数据处理框架&#xff0c;虽然能够处理海量数据&#xff0c;但是其查询性能却比较低下&#xff…

算法|13.贪心

1.字典序最小的字符串连接方案 题意&#xff1a;给定一个由字符串组成的数组strs&#xff0c;必须把所有的字符串拼接起来&#xff0c;返回所有可能的拼接结果中字典序最小的结果。 public static class MyCom implements Comparator<String>{Overridepublic int compa…

【论文阅读笔记】CRFL: Certifiably Robust Federated Learning against Backdoor Attacks

个人阅读笔记&#xff0c;如有错误欢迎指出! 会议&#xff1a;PMLR 2021[2106.08283] CRFL: Certifiably Robust Federated Learning against Backdoor Attacks (arxiv.org) 问题&#xff1a; 现有的防御算法缺乏健壮性 创新&#xff1a; 证明了所提出框架得稳定性 通过马尔…

Doris实战笔记

Doris实战笔记 本文基于Doris1.2.4.1版本部署&#xff0c;后续新版本&#xff0c;有时间更新 Docker安装部署 使用现成的镜像 下载 Docker 镜像 docker pull apache/doris:build-env-ldb-toolchain-latest运行镜像&#xff08;不推荐&#xff09; docker run -it apache/…