创建ServerSocket对象

2024-03-27 08:08
文章标签 创建 对象 serversocket

本文主要是介绍创建ServerSocket对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ServerSocket类的构造方法有四种重载形式,它们的定义如下:

public ServerSocket() throws IOException
public ServerSocket(int port) throws IOException
public ServerSocket(int port, int backlog) throws IOException
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

        在上面的构造方法中涉及到了三个参数: portbacklogbindAddr。其中portServerSocket对象要绑定的端口,backlog是请求队列的长度,bindAddrServerSocket对象要绑定的IP地址。

一、通过构造方法绑定端口

通过构造方法绑定端口是创建ServerSocket对象最常用的方式。可以通过如下的构造方法来绑定端口:

public ServerSocket(int port) throws IOException

如果port参数所指定的端口已经被绑定,构造方法就会抛出IOException异常。但实际上抛出的异常是BindException。从图4.2异常类继承关系图可以看出,所有和网络有关的异常都是IOException类的子类。因此,为了ServerSocket构造方法还可以抛出其他的异常,就使用了IOException

如果port的值为0,系统就会随机选取一个端口号。但随机选取的端口意义不大,因为客户端在连接服务器时需要明确知道服务端程序的端口号。可以通过ServerSocket的toString方法输出ServerSocket对象相关的信息。下面的代码输入了和ServerSocket对象相关的信息。

ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=1320]

上面的输出结果中的addr是服务端绑定的IP地址,如果未绑定IP地址,这个值是0.0.0.0,在这种情况下,ServerSocket对象将监听服务端所有网络接口的所有IP地址。port永远是0。localport是ServerSocket绑定的端口,如果port值为0(不是输出结果的port,是ServerSocket构造方法的参数port),localport是一个随机选取的端口号。

        在操作系统中规定1 ~ 1023为系统使用的端口号。端口号的最小值是1,最大值是65535。在Windows中用户编写的程序可以绑定端口号小于1024的端口,但在Linux/Unix下必须使用root登录才可以绑定小于1024的端口。在前面的文章中曾使用Socket类来判断本机打开了哪些端口,其实使用ServerSocket类也可以达到同样的目的。基本原理是用ServerSocket来绑定本机的端口,如果绑定某个端口时抛出BindException异常,就说明这个端口已经打开,反之则这个端口未打开。
package server;import java.net.*;public class ScanPort
{public static void main(String[] args){if (args.length == 0)return;int minPort = 0, maxPort = 0;String ports[] = args[0].split("[-]");minPort = Integer.parseInt(ports[0]);maxPort = (ports.length > 1) ? Integer.parseInt(ports[1]) : minPort;for (int port = minPort; port <= maxPort; port++)try{ServerSocket serverSocket = new ServerSocket(port);serverSocket.close();}catch (Exception e){System.err.println(e.getClass());System.err.println("端口" + port + "已经打开!");}}
}

在上面的代码中输出了创建ServerSocket对象时抛出的异常类的信息。ScanPort通过命令行参数将待扫描的端口号范围传入程序,参数格式为:minPort-maxPort,如果只输入一个端口号,ScanPort程序只扫描这个端口号。

测试:

java server.ScanPort 1-1023
运行结果:

class java.net.BindException
端口80已经打开!
class java.net.BindException
端口135已经打开!

二、设置请求队列的长度

在编写服务端程序时,一般会通过多线程来同时处理多个客户端请求。也就是说,使用一个线程来接收客户端请求,当接到一个请求后(得到一个Socket对象),会创建一个新线程,将这个客户端请求交给这个新线程处理。而那个接收客户端请求的线程则继续接收客户端请求,这个过程的实现代码如下:

ServerSocket serverSocket = new ServerSocket(1234);   // 绑定端口
// 处理其他任务的代码
while(true)
{Socket socket = serverSocket.accept(); // 等待接收客户端请求// 处理其他任务的代码new ThreadClass(socket).start();   // 创建并运行处理客户端请求的线程
}
        上面代码中 ThreadClass 类是Thread 类的子类,这个类的构造方法有一个Socket 类型的参数,可以通过构造方法将Socket 对象传入ThreadClass 对象,并在ThreadClass 对象的run 方法中处理客户端请求。这段代码从表面上看好象是天衣无缝,无论有多少客户端请求,只要服务器的配置足够高,就都可以处理。但仔细思考上面的代码 ,我们可能会发现一些问题。如果在第2行和第6行有足够复杂的代码,执行时间也比较长,这就意味着服务端程序无法及时响应客户端的请求。

假设第2行和第6行的代码是Thread.sleep(3000),这将使程序延迟3秒。那么在这3秒内,程序不会执行accept方法,因此,这段程序只是将端口绑定到了1234上,并未开始接收客户端请求。如果在这时一个客户端向端口1234发来了一个请求,从理论上讲,客户端应该出现拒绝连接错误,但客户端却显示连接成功。究其原因,就是这节要讨论的请求队列在起作用。

        在使用 ServerSocket对象绑定一个端口后,操作系统就会为这个端口分配一个先进先出的队列(这个队列长度的默认值一般是50),这个队列用于保存未处理的客户端请求,因此叫请求队列。而ServerSocket类的accept方法负责从这个队列中读取未处理的客户端请求。如果请求队列为空,accept 则处于阻塞状态。每当客户端向服务端发来一个请求,服务端会首先将这个客户端请求保存在请求队列中,然后accept 再从请求队列中读取。这也可以很好地解释为什么上面的代码在还未执行到accept 方法时,仍然可以接收一定数量的客户端请求。如果请求队列中的客户端请求数达到请求队列的最大容量时,服务端将无法再接收客户端请求。如果这时客户端再向服务端发请求,客户端将会抛出一个 SocketException 异常

        ServerSocket类有两个构造方法可以使用backlog参数重新设置请求队列的长度。在以下几种情况,仍然会采用操作系统限定的请求队列的最大长度:

  • backlog的值小于等于0
  • backlog的值大于操作系统限定的请求队列的最大长度
  • ServerSocket构造方法中未设置backlog参数

        下面的代码演示了请求队列的一些特性,请求队列长度通过命令行参数传入SetRequestQueue

package server;import java.net.*;class TestRequestQueue
{public static void main(String[] args) throws Exception{for (int i = 0; i < 10; i++){Socket socket = new Socket("localhost", 1234);socket.getOutputStream().write(1);System.out.println("已经成功创建第" + String.valueOf(i + 1) + "个客户端连接!");}}
}
public class SetRequestQueue
{public static void main(String[] args) throws Exception{if (args.length == 0)return;int queueLength = Integer.parseInt(args[0]);ServerSocket serverSocket = new ServerSocket(1234, queueLength);System.out.println("端口(1234)已经绑定,请按回车键开始处理客户端请求!");System.in.read();int n = 0;while (true){System.out.println("<准备接收第" + (++n) + "个客户端请求!");Socket socket = serverSocket.accept();System.out.println("正在处理第" + n + "个客户端请求");Thread.sleep(3000);System.out.println("第" + n + "个客户端请求已经处理完毕!>");}}
}
测试(按以下步骤操作)

        1、 执行如下命令(在执行这条命令后,先不要按回车键):

java server.SetRequestQueue 2
运行结果:

端口(1234)已经绑定,请按回车键开始处理客户端请求!
        2、 执行如下命令:

java server.TestRequestQueue
运行结果:


已经成功创建第1个客户端连接!
已经成功创建第2个客户端连接!
Exception in thread "main" java.net.SocketException: Connection reset by peer: socket write errorat java.net.SocketOutputStream.socketWrite0(Native Method)at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)at java.net.SocketOutputStream.write(SocketOutputStream.java:115)at server.TestRequestQueue.main(SetRequestQueue.java:12)

        3、按回车键继续执行SetRequestQueue后,运行结果如下:

端口(1234)已经绑定,请按回车键开始处理客户端请求!
<准备接收第1个客户端请求!
正在处理第1个客户端请求
第1个客户端请求已经处理完毕!>
<准备接收第2个客户端请求!
正在处理第2个客户端请求
第2个客户端请求已经处理完毕!>
<准备接收第3个客户端请求!
        从第二步的运行结果可以看出,当TestRequestQueue 创建两个Socket 连接之后,服务端的请求队列已满,并且服务端暂时无法继续执行(由于System.in.read() 的原因而暂停程序的执行,等待用户的输入)。因此,服务端程序无法再接收客户端请求。这时TestRequestQueue 抛出了一个SocketException 异常。在TestRequestQueue 已经创建成功的两个Socket 连接已经保存在服务端的请求队列中。在这时按任意键继续执行SetRequestQueue accept 方法就会从请求队列中将这两个客户端请求队列中依次读出来。从第三步的运行结果可以看出,服务端处理完这两个请求后(一个<…> 包含的就是一个处理过程),请求队列为空,这时accept 处理阻塞状态,等待接收第三个客户端请求。如果这时再运行TestRequestQueue ,服务端会接收几个客户端请求呢?如果将请求队列的长度设为大于10 的数,TestRequestQueue 的运行结果会是什么呢?读者可以自己做一下这些实验,看看和自己认为的结果是否一致。

三、绑定IP地址

在有多个网络接口或多个IP地址的计算机上可以使用如下的构造方法将服务端绑定在某一个IP地址上:

public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
        bindaddr参数就是要绑定的 IP地址。如果将服务端绑定到某一个IP地址上,就只有可以访问这个IP地址的客户端才能连接到服务器上。如一台机器上有两块网卡,一块网卡连接内网,另一块连接外网。如果用Java实现一个Email服务器,并且只想让内网的用户使用它。就可以使用这个构造方法将ServerSocket对象绑定到连接内网的IP地址上。这样外网就无法访问Email服务器了。可以使用如下代码来绑  定IP地址:
ServerSocket serverSocket = new
ServerSocket(1234, 0, InetAddress.getByName("192.168.18.10"));

        上面的代码将IP地址绑定到了192.168.18.10上,因此,服务端程序只能使用绑定了这个IP地址的网络接口进行通讯。


四、默认构造方法的使用

        除了使用ServerSocket类的构造方法绑定端口外,还可以用ServerSocketbind方法来完成构造方法所做的工作。要想使用bind方法,必须得用ServerSocket类的默认构造方法(没有参数的构造方法)来创建ServerSocket对象。bind方法有两个重载形式,它们的定义如下:

public void bind(SocketAddress endpoint) throws IOException
public void bind(SocketAddress endpoint, int backlog) throws IOException 

        bind方法不仅可以绑定端口,也可以设置请求队列的长度以及绑定IP地址。bind方法的作用是为了在建立ServerSocket对象后设置ServerSocket类的一些选项。而这些选项必须在绑定端口之前设置,一旦绑定了端口后,再设置这些选项将不再起作用。下面的代码演示了bind方法的使用及如何设置ServerSocket类的选项。

ServerSocket serverSocket1 = new ServerSocket();
serverSocket1.setReuseAddress(true);
serverSocket1.bind(new InetSocketAddress(1234));
ServerSocket serverSocket2 = new ServerSocket();
serverSocket2.setReuseAddress(true);
serverSocket2.bind(new InetSocketAddress("192.168.18.10", 1234));
ServerSocket serverSocket3 = new ServerSocket();
serverSocket3.setReuseAddress(true);
serverSocket3.bind(new InetSocketAddress("192.168.18.10", 1234), 30);    

        在上面的代码中设置了SO_REUSEADDR选项(这个选项将在后面的文章中详细讨论)。如果使用下面的代码,这个选项将不起作用。

ServerSocket serverSocket3 = new ServerSocket(1234);
serverSocket3.setReuseAddress(true);
        在第6 行绑定了IP 地址和端口。使用构造方法是无法得到这个组合的(想绑定IP 地址,必须得设置backlog 参数),因此,bind 方法比构造方法更灵活。


这篇关于创建ServerSocket对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/851453

相关文章

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

批处理以当前时间为文件名创建文件

批处理以当前时间为文件名创建文件 批处理创建空文件 有时候,需要创建以当前时间命名的文件,手动输入当然可以,但是有更省心的方法吗? 假设我是 windows 操作系统,打开命令行。 输入以下命令试试: echo %date:~0,4%_%date:~5,2%_%date:~8,2%_%time:~0,2%_%time:~3,2%_%time:~6,2% 输出类似: 2019_06

ORACLE 11g 创建数据库时 Enterprise Manager配置失败的解决办法 无法打开OEM的解决办法

在win7 64位系统下安装oracle11g,在使用Database configuration Assistant创建数据库时,在创建到85%的时候报错,错误如下: 解决办法: 在listener.ora中增加对BlueAeri-PC或ip地址的侦听,具体步骤如下: 1.启动Net Manager,在“监听程序”--Listener下添加一个地址,主机名写计

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

PHP7扩展开发之对象方式使用lib库

前言 上一篇文章,我们使用的是函数方式调用lib库。这篇文章我们将使用对象的方式调用lib库。调用代码如下: <?php $hello = new hello(); $result = $hello->get(); var_dump($result); ?> 我们将在扩展中实现hello类。hello类中将依赖lib库。 代码 基础代码 这个扩展,我们将在say扩展上增加相关代码。sa