第8章 客户端socket

第8章 客户端Socket

使用Socket

Socket是两台主机之间的一个连接。它可以完成7个基本操作:

* 连接远程机器

* 发送数据

* 接收数据

* 关闭连接

* 绑定端口

* 监听入站数据

* 在绑定端口上接受来自远程机器的连接

Java的Socket类(客户端和服务端都可以使用)提供了对应前4个操作的方法。后面3个操作仅服务器需要,即等待客户端的连接。这些操作有ServerSocket类实现。

Java程序通常采用以下方式使用客户端socket:

*程序用构造函数创建一个新的Socket

*Socket尝试连接远程主机

一旦建立了连接,本地和远程主机就从这个socket得到输入流和输出流,使用这两个流相互发送数据。

用Telnet研究协议

用Socket从服务器读取

在端口13打开与time.nist.gov的连接

Socket  socket = new Socket("time.nist.gov",13);

使用setSoTimeout()方法为连接设置一个超时时间。

socket.setSoTimeout(15000);

调用getInputStream()来返回一个InputStream,用它从socket读取字节。

示例8-1:

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">import java.net.*;
import java.io.*;

public class DaytimeClient {

  public static void main(String[] args) {

    String hostname = args.length > 0 ? args[0] : "time.nist.gov";
    Socket socket = null;
    try {
      socket = new Socket(hostname, 13);
      socket.setSoTimeout(15000);
      InputStream in = socket.getInputStream();
      StringBuilder time = new StringBuilder();
      InputStreamReader reader = new InputStreamReader(in, "ASCII");
      for (int c = reader.read(); c != -1; c = reader.read()) {
        time.append((char) c);
      }
      System.out.println(time);
    } catch (IOException ex) {
      System.err.println(ex);
    } finally {
      if (socket != null) {
        try {
          socket.close();
        } catch (IOException ex) {
          // ignore
        }
      }
    }
  }
}</span></span></span>

用Socket写入服务器

示例8-4:一个基于网络的英语-拉丁语翻译程序

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">import java.io.*;
import java.net.*;

public class DictClient {

  public static final String SERVER = "dict.org";
  public static final int PORT = 2628;
  public static final int TIMEOUT = 15000;
  
  public static void main(String[] args) {

    Socket socket = null;
    try {
      socket = new Socket(SERVER, PORT);
      socket.setSoTimeout(TIMEOUT);
      OutputStream out = socket.getOutputStream();
      Writer writer = new OutputStreamWriter(out, "UTF-8");
      writer = new BufferedWriter(writer);
      InputStream in = socket.getInputStream();
      BufferedReader reader = new BufferedReader(
          new InputStreamReader(in, "UTF-8"));
      
      for (String word : args) {
        define(word, writer, reader);
      }
      
      writer.write("quit\r\n");
      writer.flush();
    } catch (IOException ex) {
      System.err.println(ex);
    } finally { // dispose
      if (socket != null) {
        try {
          socket.close();
        } catch (IOException ex) {
          // ignore
        }
      }
    }
  }

  static void define(String word, Writer writer, BufferedReader reader)
      throws IOException, UnsupportedEncodingException {
    writer.write("DEFINE eng-lat " + word + "\r\n");
    writer.flush();

    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
      if (line.startsWith("250 ")) { // OK
        return;
      } else if (line.startsWith("552 ")) { // no match
        System.out.println("No definition found for " + word);
        return;
      }
      else if (line.matches("\\d\\d\\d .*")) continue;
      else if (line.trim().equals(".")) continue;
      else System.out.println(line);
    }
  } 
}</span></span></span>

半关闭Socket

close()方法同时关闭Socket的输入和输出。

shutdownInput()和shutdownOutput()方法只关闭连接的一半。

构造和连接Socket

 Java.net.Socket类是Java完成客户端TCP操作的基础类。其他建立TCP网络连接的面向客户端的类(如URL、URLConnection、Applet和JEdittorPane)最终都会调用这个类的方法。

基本构造函数

 public Socket(String host,int port) throwsUnknownHostException,IOException

 public Socket(InetAddress host,int port)throws IOException

这些构造函数会连接socket(也就是说,在构造函数返回之前,会在远程主机建立一个活动的网络连接)。

有3个构造函数可以创建未连接的Socket。

 public Socket()

 public Socket(Proxy proxy)

 protected Socket(SocketImpl impl)

选择从哪个本地接口连接

构造但不连接

 public Socket()

再为某个connect()方法传入一个SocketAddress来建立连接。

Socket地址

SocketAddress类的主要用途是为暂时的socket连接信息提供一个方便的存储。

public SocketAddressgetRemoteSocketAddress() 返回所连接系统的地址

public SocketAddressgetLocalSocketAddress() 返回发起连接的地址

实际使用的Socket地址都是InetSocketAddress的实例。

通常会用一个主机和一个端口(对于客户端)或者只使用一个端口(对于服务器)来创建InetSocketAddress类:

 public InetSocketAddress(InetAddressaddress,int port)

 public InetSocketAddress(String host,int port)

 public InetSocketAddress(int port)

代理服务器

 例如:下面的代码段使用位于myproxy.example.com的SOCKS代理服务器来连接主机login.ibiblio.org

     SocketAddressproxyAddress  = new InetSocketAddress(“myproxy.example.com”,1080);

     Proxyproxy = new Proxy(Proxy.Type.SOCKS,proxyAddress)

     Sockets = new Socket(proxy);

     SocketAddressremote = new InetSocketAddress(“login.ibiblio.org”,25);

     s.connect(remote);

获取Socket的信息

Socket对象有一些属性可以通过获取方法来访问:

public InetAddressgetInetAddress()

*远程地址

public intgetPort()

*远程端口

public InetAddressgetLocalAddress()

*本地地址

public intgetLocalPort()

*本地端口

关闭还是连接

如果socket关闭,isClosed()方法会返回true,否则返回false.

isConnected()方法,指出Socket是否从未连接过一个远程主机。

IsBound()会告诉你Socket是否成功地绑定到本地系统上的出站端口。

设置Socket选项

对于客户端Socket,Java支持9个选项:

*TCP_NODELAY

*SO_BINDADDR

*SO_TIMEOUT

*SO_LINGER

*SO_SNDBUF

*SO_RCVBUF

*SO_KEEPALIVE

*OOBINLINE

*IP_TOS

TCP_NODELAY

 Public void setTcpNoDelay(Boolean on) throwsSocketExcpetion

 Public Boolean getTcpNoDelay() throwsSocketException

 设置TCP_NODELAY为true可确保包会尽可能地发送,而无论包的大小。

 setTcpNoDelay(true)关闭了Socket的缓冲。setTcpNoDelay(false)再次启用缓冲。

SO_LINGER

       public void setSoLinger(Boolean on,intseconds) throws SocketException

      publicint getSoLinger() throws SocketException

       SO_LINGER选项指定了Socket关闭时如何处理尚未发送的数据报

SO_TIMEOUT

       public void setSoTimeout(intmilliseconds) throws SocketException

       public int getSoTimeout() throwsSocketException

SO_RCVBUF和SO_SNDBUF

       public void setReceiveBufferSize(int sie)throws SocketException,IllegalArgumentException

      publicint getReceiveBufferSize() throws SocketException

       public void setSendBufferSize(int size)throws SocketException,illegalArgumentException

       public int getSendBufferSize() throwsSocketException

SO_KEEPALIVE

       如果打开了SO_KEEPALIVE,客户端偶尔会通过一个空闲连接发送一个数据包(一般两小时一次),以确保服务器未崩溃。如果服务器没响应这个包,客户端会持续尝试11分钟多的时间,直接接收响应为止。如果在12分钟内未收到响应,客户端就关闭socket。如果没有SO_KEEPALIVE,不活动的客户端可能会永远存在下去,而不会注意到服务器已经崩溃。

       public void setKeepAlive(Boolean on )throws SocketException

       public Boolean getKeepAlive() throwsSocketException

OOBINLINE

       如果你希望接收正常数据中的紧急数据,就需要使用下面的方法设置OOBINLINE选项为true:

       public void setOOBInline(Boolean on)throws SocketException

       public Boolean getOOBInline() throwsSocketException

       一旦打开OOBInline,到达的任何紧急数据就将以正常方式放在Socket的输入流中等待读取。

SO_REUSEADDR

       如果开启SO_REUSEADDR,就允许另一个Socket绑定到这个端口,及时此时仍有可能存在前一个Socket未接收的数据。

       public void setReuseAddress(Boolean on )throws SocketException

       public Boolean gerReuseAddress() throwsSocketException

TP_TOS服务类型

       服务类型存储在IP首部中一个名为IP_TOS的8位字段中。Java允许你使用下面两个方法检测和设置Socket放在这个字段中的值:

       public int getTrafficClass() throwsSocketExcetpion

       public void setTrafficClass(inttrafficClass) throws SocketException

Socket异常

你可能感兴趣的:(第8章 客户端socket)