zoukankan      html  css  js  c++  java
  • 客户端Socket

    导语

    java.net.Socket类是JAVA完成客户端TCP操作的基础类。其他建立TCP网络连接的类(如URL,URLConnection和EditorPane)最终会调用这个类的方法。这个类本身使用原生代码与主机操作系统的本地TCP栈进行通信

    基本构造函数

    每个Socket构造函数指定要连接的主机和端口。主机可以指定InetAddress或主机名,端口可以指定1到65535之间的int值。

    pubic Socket(String host, int port)
    public Sokcet(InetAddress host, int port)
    

    示例代码

    public static void main(String[] args) {
    		try {
    			InetAddress inet = InetAddress.getByName("www.xdysite.cn");
    			Socket toMy = new Socket(inet, 80);
    		} catch (UnknownHostException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    在示例代码中创建一个Socket连接到www.xdysite.cn主机

    构造但不连接

    目前为止我们讨论的所有构造函数在创建Socket对象时会打开一个与远程主机的网络连接。如果想要分解创建对象与连接这两个操作,可以不为Socket提供任何参数。在将来的某个时刻再提供主机地址和端口。

    public static void main(String[] args) {
    		try (Socket socket = new Socket()){
    			SocketAddress sa = new InetSocketAddress("www.xdysite.cn", 80);
    			//配置Socket连接
    			socket.setSoTimeout(15000);
    			//连接服务器
    			socket.connect(sa);
    		} catch (UnknownHostException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    

    上面的代码中只有调用了connect方法后采取连接目标机

    连接超时处理

    connet方法还有一个参数来指定连接超时时间。connect(SocketAddress endpoint, int timeout)方法中的第二个参数如果为0的话将会无限等待下去,直到连接成功。否则指定一个大于0的参数来设置超时时间。在使用Socket的的有参构造器时都会调用connet方法并timeout设置为0,这点在编程时需要注意。

    使用代理服务器

    一般情况下,Socket使用的代理服务器可以由socksProxyHostsocksProxyPort系统属性来控制,这些属性应用于系统中所有的Socket。但是如果我们需要使用别的代理的话,也可以用 Socket()来指定。下面就是一个简单的示例代码。

    SocketAddress proxyAddress = new InetSocketAddress("myproxy.example.cn", 1080);
    		Proxy proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress);
    		Socket s = new Socket(proxy);
    		SocketAddress remote = new InetSocketAddress("www.xdysite.cn", 9000);
    		try {
    			s.connect(remote);
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				s.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    

    设置Socket选项

    JAVA中的Socket选项依赖于操作系统的Socket选项,因为JAVA中的Socket最终还是要调用操作系统中的API。下面是客户端Sokcet可以设置的一些选项。

    • TCP_NODELAY
    • SO_BINDADDR
    • SO_TIMEOUT
    • SO_LINGER
    • SO_SNDBUF
    • SO_RCVBUF
    • SO_KEEPALIVE
    • OOBINLINE
    • IP_TOS

    这些名字来自于UNIX系统所使用的C头文件中的命名常量,因为Socket来自于UNIX操作系统。下面对这几个常量进行说明。

    TCP_NODELAY

    设置TCP_NODELAY为true时可确保包尽可能快的发送(不进行缓冲,一有数据就发)。正常情况下,为了提高吞吐量,小数据包(比如一字节)在发送前会组合为更大的包。在发送另一个包之前,本地主机要等待远程系统对前一个包的确认。这种算法被称为Nagle算法。这种算法在某些情况下存在严重的问题。比如远程桌面系统。服务器要实时扑住客户端鼠标移动的操作。如果使用缓冲在加上网速很慢的话,那么最后的效果就很差强人意了。

    public void setTcpNoDelay(boolean on)
    

    通过上面的方法来设置该参数的值,如果底层Socket不支持TCP_NODELAY选项,则会抛出异常

    SO_TIMEOUT

    正常情况下,调用read()方法从Socket 读取数据时都会阻塞,直到有数据时才返回。设置SO_TIMEOUT可以确保阻塞的时间不会超过某个固定的毫秒数。当时间到期时就会抛出一个InterruptedIOException异常。不过,即使超时了,Socket仍是连接的,所以可以再次调用read()去读数据。

    SO_LINGER

    public void setSoLinger(boolean on, int seconds)
    

    选项SO_LINGER指定了Socket关闭时如何处理尚未发送的数据报。默认情况下,close()方法将立即返回,但系统仍会尝试发送剩余的数据。如果通过设置函数将其设置为false(第二个参数将不起作用),那么当 Socket关闭的时候,所有未发送的数据包都将被丢弃。如果将其设置为true,并指定一个时间,那么 close()方法将会阻塞(阻塞的时间为指定的秒数)。如果时间一到, Socket将会关闭,所有剩余的数据将不会发送,也不会接受数据。

    SO_KEEPALIVE

    如果打开SO_KEEPALIVE,客户端偶尔会通过一个空闲的连接发送一个数据包(一般是两小时一次),以确保服务器未崩溃。如果服务器没有响应这个包,客户端会在将来的11分钟内持续尝,直到接收到响应为止。如果在12分钟内未收到响应,客户端就会关闭socket。SO _KEEPALIVE默认值是false,这表明客户端不会检查服务器是否挂掉。

    public void setKeepAlive(boolean on)
    

    Socket异常

    Socket类的大多数方法都声明抛出IOException或其子类java.net.SocketException。不过,仅仅知道发生了问题,这对于处理问题往往是不够的。是不是因为远程主机忙而拒绝连接?还是因为没有服务在这个端口上监听而导致远程主机拒绝连接?或者是因为网络拥塞或主机崩溃而导致连接超时?SocketException有几个子类。可以对出现的问题以及为什么出现问题提供有关的信息:

    public class BindException extends SocketException
    public class ConnectException extends SocketException
    public class NoRouteToHostException extends SocketException
    

    如果试图在一个正在使用的端口上构造Socket或ServerSocket对象,或者你没有足够的权限使用这个端口,就会抛出BindException异常。当连接被远程主机拒绝,而拒绝的原因通常是由于主机忙或者是没有进程监听这个端口,此时就会抛出ConnectionException异常。最后一点, NoRouteToHostException异常是因为连接已过期。

    一个简单的示例

    通过这个示例,我们练习一下客户端编程。whois服务器的功能是进行域名的查询,下面是通过telnet的方式来查询一个域名的信息。

    telnet查询域名信息

    我们通过telnet连接到whois服务器,查询了域名为xdysite.cn的信息。下面我们通过java来实现这一查询功能。

    public static void main(String[] args) {
    	try (Socket socket = new Socket("ewhois.cnnic.cn", 43)) {
    		Writer writer = new OutputStreamWriter(socket.getOutputStream());
    		writer.write("xdysite.cn
    ");
    		writer.flush();
    		Reader reader = new InputStreamReader(socket.getInputStream(), "utf-8");
    		int c = 0;
    		while ((c = reader.read()) != -1) {
    			System.out.print((char)c);
    		}
    		writer.close();
    		reader.close();
    	} catch (UnknownHostException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    }
  • 相关阅读:
    面试技巧
    JDK1.8新特性(一): 接口的默认方法default
    idea2019.2安裝MybatisCodeHelper插件
    为什么要拆掉“烟囱式”系统
    git
    springboot-使用AOP日志拦截实现
    为何放弃Eclipse,选择IntelliJ IDEA,看完终于明白了
    StringUtils.isBlank()的使用
    count(1)、count(*)、count(字段)的区别
    关于redis中zset底层跳表的理解
  • 原文地址:https://www.cnblogs.com/xidongyu/p/6194590.html
Copyright © 2011-2022 走看看