目录
ServerSocket类的使用
1.1 aacept与超时Timeout
**public Socket accept()**作用就是监听并接受套接字的连接,此方法在连接传入前一直阻塞
**setSoTimeout(timeout)**设置超时时间,设置后accept()最大只会阻塞timeout个时间,超过时间就会抛出异常。默认值为0表示无限等待
int getSoTimeout() 获取超时时间
@Test
public void test11(){
try {
ServerSocket serverSocket = new ServerSocket(8888);
//设置超时时间
serverSocket.setSoTimeout(3000);
System.out.println("超时时间 "+serverSocket.getSoTimeout());
serverSocket.accept();
} catch (IOException e) {
if(e.getClass() == SocketTimeoutException.class)
System.out.println("超时");
else
e.printStackTrace();
}
}
1.2 构造方法backlog的含义
public ServerSocket(int port,int backLog)
参数backlog的主要作用就是允许接收客户端请求的个数(默认值50)。客户端在连接服务器时,客户端的请求要到操作系统的队列中,当执行accept()方法。允许多少客户端连接要好看backlog是多少。
测试代码如下
服务端
在服务端要在accept()让线程休眠。目的就是模拟在accpet同时来了5个请求–结果在客户端发现出现异常显示:refuse Connect,且只有三个线程被处理了
@Test
public void test9(){
try {
ServerSocket serverSocket = new ServerSocket(8888,3);
TimeUnit.SECONDS.sleep(5);
Socket client = serverSocket.accept();
System.out.println("新客户端连接1 -> "+client.getPort());
Socket client2 = serverSocket.accept();
System.out.println("新客户端连接2 -> "+client2.getPort());
Socket client3 = serverSocket.accept();
System.out.println("新客户端连接3 -> "+client3.getPort());
Socket client4 = serverSocket.accept();
System.out.println("新客户端连接4 -> "+client4.getPort());
Socket client5 = serverSocket.accept();
System.out.println("新客户端连接5 -> "+client5.getPort());
}catch (IOException | InterruptedException e){
e.printStackTrace();
}
}
客户端
@Test
public void test10(){
try {
Socket socket1 = new Socket("localhost", 8888);
Socket socket2 = new Socket("localhost", 8888);
Socket socket3 = new Socket("localhost", 8888);
Socket socket4 = new Socket("localhost", 8888);
Socket socket5 = new Socket("localhost", 8888);
}catch (IOException e){
e.printStackTrace();
}
}
1.3 构造方法的InetAddress
public ServerSocket(int port,int backlog,InetAddress bindAddr)
其中bindAddr的作用是使用指定Port和backlog将Socket绑定到本地的InetAddress来创建服务器。
在之前我们不指定InetAddress时访问此时服务器,在本地上可以使用lockhost或者127.0.0.1或者本机的ip地址,比如192.168.1.4
但是当我们指定了InetAddress时就不能使用其他IP地址了,只能使用指定的IP地址
代码测试分析
当客户端用127.0.0.1是可以访问服务器的,但是当使用192.168.1.4就不行了,因为此时IP对应不上,客户端抛出异常
服务器代码如下
@Test
public void test12(){
ServerSocket serverSocket = null;
try {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
serverSocket = new ServerSocket(8888,3,inetAddress);
serverSocket.accept();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(serverSocket != null){
serverSocket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
客户端代码如下
@Test
public void test13(){
Socket socket = null;
try {
socket = new Socket("192.168.1.4", 8888);
socket.close();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(socket!= null){
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
1.4 绑定到指定的Socket地址
public void bind(SocketAddress endpoint)方法主要用来将ServerSocket绑定到指定的socket地址(IP和端口),使用这个地址月客户端进行通信。
如果地址为null,则系统将挑选一个临时端口和一个有效本地地址来绑定套接字。
该方法使用场景就是在使用ServerSocket类的无参构造方法之后想指定本地端口。
注意:InetAddress类表示IP地址,而InetSocketAddress表示Socket地址。
InetAddress是SocketAddress的实现类
代码测试
服务端代码如下
@Test
public void test14(){
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8888));
Socket client = serverSocket.accept();
System.out.println("客户端连接 "+client.getPort());
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(serverSocket != null){
serverSocket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
客户端代码如下
@Test
public void test15(){
Socket socket = null;
try {
socket = new Socket("localhost",8888);
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(socket != null){
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
1.5 绑定到指定的socket地址并设置backlog数量
bind(SocketAddress endpoint,int backlog)方法不仅可以绑定到指定IP,而且还可以设置backlog的连接数量
代码测试
略
1.6 获取本地的SocketAddress对象以及本地端口
getLocalSocketAddress()方法用来获取本地的Socket对象,如果此时ServerSocket尚未绑定则返回null。getLocalport用来返回Socket绑定到本地的端口
测试代码
@Test
public void test16(){
try {
ServerSocket serverSocket = new ServerSocket();
System.out.println("空构造 localPort -> "+serverSocket.getLocalPort());
System.out.println("空构造 socketAddress -> "+serverSocket.getLocalSocketAddress());
ServerSocket serverSocket1 = new ServerSocket(8888);
System.out.println(serverSocket1.getLocalPort());
System.out.println(serverSocket1.getLocalSocketAddress());
serverSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
1.7 InetSocketAddress类的使用
InetSocketAddress是一个套接字地址(IP+端口号)。还可以是(主机名+端口)。
SocketAddress与InetAddress本质的区别就是SocketAddress不基于任何协议。
1构造方法
InetSocketAddress(InetAddress addr,int port)
2.getHostName()和getHostString()方法区别
public final String getHostName()方法的作用是获取主机名,注意:如果地址是用字面IP地址创建的,则可能触发反向查找,利用DNS通过IP找到域名。
public final String getHostString()方法的作用是返回主机名或地址的字符串形式,如果灭有主机名就返回IP地址,这样的好处是不会反向查找。
代码测试
如下所示:倘若先获取的是hostName在获取hostString会同时打印hostName的内容。如果先获取hostString()此时hostString是打印的IP地址
分析:
因为hostName会进行反向查找,查到主机名了。
如下所示hostString会判断是不是有hostName,如果先获取hostName了此时这个值就不为空了
@Test
public void test17(){
InetSocketAddress inetSocketAddress = new InetSocketAddress("192.168.1.102",8888);
System.out.println("hostString -> "+inetSocketAddress.getHostString());
System.out.println("hostName -> "+inetSocketAddress.getHostName());
}
3.获取IP地址InetAddress对象
public final InetAddress getAddress()方法的作用是获取InetAddress对象
测试代码如下
@Test
public void test18(){
InetSocketAddress inetSocketAddress = new InetSocketAddress("192.168.1.102", 8888);
System.out.println(inetSocketAddress.getAddress());
}
4.创建未解析的套接字地址
public static InetSocketAddress createUnresolved(String host,int port)方法的作用是根据主机名和端口号创建未解析的套接字地址
public final boolean isUnresolved()方法的作用:如果无法将主机名解析为InetAddress则返回true。
测试代码如下
输出 false true
第一个输出false因为是可以解析的
第二个为true是可以解析也不会去解析
@Test
public void test19(){
InetSocketAddress inetSocketAddress = new InetSocketAddress("www.baidu.com", 80);
System.out.println(inetSocketAddress.isUnresolved());
InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("www.baidu.com", 80);
System.out.println(socketAddress.isUnresolved());
}
1.8 关闭与关闭状态获取
public void close()方法的作用是关闭此套接字。在accept()中所有当前阻塞的线程都会抛出SocketException。如果此套接字有一个与之关联的通道,则关闭该通道。
public boolean isClosed()方法的作用是返回ServerSocket的关闭状态。如果已经关闭了套接字,就返回true
如下代码打印:false true
@Test
public void test29() throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println(serverSocket.isClosed());
serverSocket.close();
System.out.println(serverSocket.isClosed());
}
1.9 判断Socket的绑定状态
public bolelan isBound()方法的作用是返回ServerSOcket的绑定状态。如果ServerSocket成功绑定到一个地址,则返回true。
如下代码所示打印:false true
@Test
public void test20(){
try {
ServerSocket serverSocket = new ServerSocket();
System.out.println(serverSocket.isBound());
serverSocket.bind(new InetSocketAddress(8888));
System.out.println(serverSocket.isBound());
} catch (IOException e) {
e.printStackTrace();
}
}
1.10 获取IP地址
getInetAddress()获取绑定到Socket的IP地址信息
如果Socket是未绑定的就返回null。
代码:略
1.11 Socket选项ReuseAddress
public void setReuseAddress(boolean on)方法的作用是启用/禁用SO_REUSEADDR套接字选项。
关闭TCP连接时该连接可能在关闭后的一段时间内保持超时状态(通常称为TIME_WAIT或2MSL等待状态)。
如果存在超时状态的连接,则应用程序可能不能将套接字绑定到所需的SocketAddress上
如果使用bind(SocketAddress)之前启用SO_REUSEADDR就可以绑定处于超时状态的套接字。
注意:当创建ServerSocket时其SO_REUSEADDR选项是不确定的,依赖于操作系统的实现。
可以使用getReuseAddress()来判断SO_REUSEADDR的初始值
在SOcket的close或关闭连接和端口但是在操作系统上不会立刻释放端口。如果端口是TIME_WAIT状态,则在linux上是可以重用此端口。
如下代码需要再linux中测试
客户端的端口不允许被复用
服务端的端口允许被复用
1.12 Socket选项RecieveBufferSize
public void setReceiveBufferSize(int size)
此方法是从此ServerSocket接受的套接字的SSO_RCVBUF选项设置新的建议值。实际被采纳的值必须在accept()方法返回套接字后通过Socket.getReceiveBufferSize()方法进行获取。
SO_RCVBUF的值用于设置内部套接字接受缓冲区的大小和设置公布到远程同位体的TCP接受窗口的大小。
代码:略