ServerSocket.class实现了服务器套接字的功能。服务器套接字会等待通过网络传来的请求。它会根据该请求执行一些操作,然后可能向请求者返回结果。ServerSocket.class的UML关系图如下所示:
一、构造函数
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;ServerSocket(SocketImpl impl);
复制代码 1、int port指定服务器要绑定的端口(即服务器要监听的端口),如果运行时无法绑定1~65536之间的某个端口号,会抛出BindException(它是IOException的子类),如下所示:
BindException一般是由以下2种原因造成的:
①、端口已经被其他服务器进程占用。
②、在某些操作系统中,如果没有以超级用户的身份来运行服务器程序,那么操作系统不允许服务器绑定到1~1023之间的端口。
2、int backlog指定客户连接请求队列的长度,如果没有指定,默认值是50;
3、InetAddress bindAddr指定服务器要绑定的IP地址;
二、客户连接请求队列的长度
管理客户连接请求的任务是由操作系统来完成的。操作系统把这些连接请求存储在一个先进先出的队列中。许多操作系统都限定了队列的最大长度,一般为50。当队列中的连接请求达到了队列的最大长度时,服务器进程所在的主机会拒绝新的连接请求。只有当服务器进程通过ServerSocket的accept()函数从队列中取出连接请求,使队列腾出空位,队列才能继续加入新的连接请求。如下代码所示:
- package com.xxx.serverSocket;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class Server { private ServerSocket serverSocket = null; public Server() throws IOException { this.serverSocket = new ServerSocket(8080,3);//端口号为8080,请求连接的最大长度为3 System.out.println("服务器启动"); } public void service() { while (true) { Socket socket = null; try { socket = serverSocket.accept(); System.out.println("New connection accepted " + socket.getInetAddress() + ":" + socket.getPort()); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close();//无论如何,最终都会关闭这个Socket } catch (IOException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws Exception { Server server = new Server(); Thread.sleep(600 * 1000);// server.service(); }}
复制代码 Server端运行结果如下:

- package com.xxx.serverSocket;import java.net.Socket;public class Client { public static void main(String[] args) throws Exception { Socket[] sockets = new Socket[100]; for (int i = 0; i < sockets.length; i++) { sockets[i] = new Socket("127.0.0.1", 8080); System.out.println("第" + (i + 1) + "次连接成功"); } Thread.sleep(3000); for (int i = 0; i < sockets.length; i++) { sockets[i].close(); } }}
复制代码 Cient端运行结果如下:

如果将Server端的代码修改如下(只修改了main函数中的内容,调用service()函数来处理Client端的连接Socket,并且在finally块中关闭Client端的连接Socket):- package com.xxx.serverSocket;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class Server { private ServerSocket serverSocket = null; public Server() throws IOException { this.serverSocket = new ServerSocket(8080,3);//请求连接的最大长度为3 System.out.println("服务器启动"); } public void service() { while (true) { Socket socket = null; try { socket = serverSocket.accept(); System.out.println("New connection accepted " + socket.getInetAddress() + ":" + socket.getPort()); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws Exception { Server server = new Server(); server.service(); }}
复制代码 Server端运行结果如下:
Cient端运行结果如下:
三、构造函数绑定IP和bind()函数绑定port
3.1、构造函数绑定IP
如果主机只有一个IP地址,那么在默认情况下,服务器程序就与该IP地址绑定。下面这个构造函数就是绑定IP、port、和连接队列长度的构造函数:- public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException;
复制代码 InetAddress bindAddr用来指定IP地址。该构造方法适用于具有多个IP地址的主机。假定一个主机有两个网卡,一个网卡用于连接到Internet,IP地址为222.67.5.94,另一个网卡用于连接到本地局域网,IP地址为192.168.3.4。如果服务器仅仅被本地局域网中的客户访问,那么可以按如下方式创建- ServerSocket serverSocket = new ServerSocket(8080,3,InetAddress.getByName("192.168.3.4"));
复制代码 3.2、bind()函数绑定port
ServerSocket有一个不带参数的默认构造方法。通过该方法创建的ServerSocket不与任何端口绑定,需要通过bind()函数,如下所示:- ServerSocket serverSocket = new ServerSocket();//需要先把SO_REUSEADDR选项设为true(SO_REUSEADDR在setReuseAddress()函数的源码里),才能进行port绑定serverSocket.setReuseAddress(true);serverSocket.bind(new InetSocketAddress(8000));//与8000端日进行绑定
复制代码 如果把以上程序代码改为如下所示,则会失效(因为SO_REUSEADDR选项必须在服务器绑定端口之前设置才有效):- ServerSocket serverSocket = new ServerSocket(8000);serverSocket.setReuseAddress(true);
复制代码 四、close()函数、isBound()函数、isClosed()函数
ServerSocket的close()函数使服务器释放占用的端口,并且断开与所有客户的连接。当一个服务器程序运行结束时,即使没有执行ServerSocket的close()函数,操作系统也会释放这个服务器占用的端口。因此,服务器程序并不一定要在结束之前执行ServerSocket的close()函数。
在某些情况下,如果希望及时释放服务器的端口,以便让其他程序能占用该端口,则可以显式地调用ServerSocket的close()方法。例如以下代码用于扫描1~65535之间的端口号。如果ServerSocket成功创建,则意味着该端口未被其他服务器进程绑定,否则说明该端口已经被其他进程占用,如下代码所示(个人认为这种方式不好,不如执行shell命令再解析shell命令返回的字符串高效,windows系统查看端口号的shell命令为netstat -ano):
ServerSocket的isBound()函数判断ServerSocket是否已经与一个端口绑定,只要ServerSocket已经与一个端口绑定,即使它已经被关闭,isBound()函数也会返回true。
如果需要判断一个ServerSocket是否已经与特定端口绑定,并且还没有被关闭,则可以采用以下方式:- boolean isOpen = serverSocket.isBound() && !serverSocket.isClosed();
复制代码 五、getInetAddress()函数和getLocalPort()函数
getInetAddress()函数用于获得服务器绑定的IP地址:和getLocalPort()函数用于获得服务器绑定的port,如果把port设为0,那么将由操作系统为服务器分配一个port(称为匿名port)。如下代码所示:- package com.xxx.serverSocket;import java.io.IOException;import java.net.InetAddress;import java.net.ServerSocket;public class ServerSocketTest { public static void main(String[] args) throws IOException { //把port设为0,请求连接的最大长度为3,IP设为127.0.0.1 ServerSocket serverSocket = new ServerSocket(0,3, InetAddress.getByName("127.0.0.1")); System.out.println(serverSocket.getInetAddress()); System.out.println(serverSocket.getLocalPort()); }}
复制代码 运行结果如下所示:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |