你的位置:首页 > Java教程

[Java教程]Java Socket 第一剑


  • 什么是Socket?

    API说:他是套接字,两台机器连接的端点。他的实际工作由SocketIml执行。

           没图说个**,下面用个图能说明Socket,在哪个位置:

           

  • 相关源码:

   1.Socket:

   属性:SocketImpl impl,socket的实现,就是API说的,实际工作由它执行。

publicclass Socket implements java.io.Closeable {  /**   * Various states of this socket.   */  private boolean created = false;  private boolean bound = false;  private boolean connected = false;  private boolean closed = false;  private Object closeLock = new Object();  private boolean shutIn = false;  private boolean shutOut = false;  /**   * The implementation of this Socket.   */  SocketImpl impl;  /**   * Are we using an older SocketImpl?   */  private boolean oldImpl = false;

  构造方法:public Socket(String host, int port)throws UnknownHostException, IOException

                    创建一个socket连接到host上的port端口上。这个构造方法会调自己私有的构造方法,这个私有的方法就有设置impl.

 /**   * Creates a stream socket and connects it to the specified port   * number on the named host.   * <p>   * If the specified host is {@code null} it is the equivalent of   * specifying the address as   * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.   * In other words, it is equivalent to specifying an address of the   * loopback interface. </p>   * <p>   * If the application has specified a server socket factory, that   * factory's {@code createSocketImpl} method is called to create   * the actual socket implementation. Otherwise a "plain" socket is created.   * <p>   * If there is a security manager, its   * {@code checkConnect} method is called   * with the host address and {@code port}   * as its arguments. This could result in a SecurityException.   *   * @param   host  the host name, or {@code null} for the loopback address.   * @param   port  the port number.   *   * @exception UnknownHostException if the IP address of   * the host could not be determined.   *   * @exception IOException if an I/O error occurs when creating the socket.   * @exception SecurityException if a security manager exists and its   *       {@code checkConnect} method doesn't allow the operation.   * @exception IllegalArgumentException if the port parameter is outside   *       the specified range of valid port values, which is between   *       0 and 65535, inclusive.   * @see    java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)   * @see    java.net.SocketImpl   * @see    java.net.SocketImplFactory#createSocketImpl()   * @see    SecurityManager#checkConnect   */  public Socket(String host, int port)    throws UnknownHostException, IOException  {    this(host != null ? new InetSocketAddress(host, port) :       new InetSocketAddress(InetAddress.getByName(null), port),       (SocketAddress) null, true);  }

  构造函数:private Socket(SocketAddress address, SocketAddress localAddr,boolean stream) throws IOException

         这个私有的构造函数,设置iml,绑定主机,请求连接。

  private Socket(SocketAddress address, SocketAddress localAddr,          boolean stream) throws IOException {    setImpl();    // backward compatibility    if (address == null)      throw new NullPointerException();    try {      createImpl(stream);      if (localAddr != null)        bind(localAddr);      connect(address);    } catch (IOException | IllegalArgumentException | SecurityException e) {      try {        close();      } catch (IOException ce) {        e.addSuppressed(ce);      }      throw e;    }  }

  方法: public void connect(SocketAddress endpoint, int timeout) throws IOException 

               用im请求连接。API说的有点道理,实际工作真是他完成的。这里用到了门面设计模式。

 public void connect(SocketAddress endpoint, int timeout) throws IOException {    if (endpoint == null)      throw new IllegalArgumentException("connect: The address can't be null");    if (timeout < 0)     throw new IllegalArgumentException("connect: timeout can't be negative");    if (isClosed())      throw new SocketException("Socket is closed");    if (!oldImpl && isConnected())      throw new SocketException("already connected");    if (!(endpoint instanceof InetSocketAddress))      throw new IllegalArgumentException("Unsupported address type");    InetSocketAddress epoint = (InetSocketAddress) endpoint;    InetAddress addr = epoint.getAddress ();    int port = epoint.getPort();    checkAddress(addr, "connect");    SecurityManager security = System.getSecurityManager();    if (security != null) {      if (epoint.isUnresolved())        security.checkConnect(epoint.getHostName(), port);      else        security.checkConnect(addr.getHostAddress(), port);    }    if (!created)      createImpl(true);    if (!oldImpl)      impl.connect(epoint, timeout);    else if (timeout == 0) {      if (epoint.isUnresolved())        impl.connect(addr.getHostName(), port);      else        impl.connect(addr, port);    } else      throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");    connected = true;    /*     * If the socket was not bound before the connect, it is now because     * the kernel will have picked an ephemeral port & a local address     */    bound = true;  }

  2.ServerSocket

     属性:SocketImpl impl,实际工作也是交由他去执行。
    
    构造方法:public ServerSocket(int port)
         创建一个服务端socket,绑定到指定的端口上,具体的实现,这个构造函数调用了另一个函数去执行,这个函数将会调用绑定的方法。
  /**   * Creates a server socket, bound to the specified port. A port number   * of {@code 0} means that the port number is automatically   * allocated, typically from an ephemeral port range. This port   * number can then be retrieved by calling {@link #getLocalPort getLocalPort}.   * <p>   * The maximum queue length for incoming connection indications (a   * request to connect) is set to {@code 50}. If a connection   * indication arrives when the queue is full, the connection is refused.   * <p>   * If the application has specified a server socket factory, that   * factory's {@code createSocketImpl} method is called to create   * the actual socket implementation. Otherwise a "plain" socket is created.   * <p>   * If there is a security manager,   * its {@code checkListen} method is called   * with the {@code port} argument   * as its argument to ensure the operation is allowed.   * This could result in a SecurityException.   *   *   * @param   port the port number, or {@code 0} to use a port   *          number that is automatically allocated.   *   * @exception IOException if an I/O error occurs when opening the socket.   * @exception SecurityException   * if a security manager exists and its {@code checkListen}   * method doesn't allow the operation.   * @exception IllegalArgumentException if the port parameter is outside   *       the specified range of valid port values, which is between   *       0 and 65535, inclusive.   *   * @see    java.net.SocketImpl   * @see    java.net.SocketImplFactory#createSocketImpl()   * @see    java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory)   * @see    SecurityManager#checkListen   */  public ServerSocket(int port) throws IOException {    this(port, 50, null);  }

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

      用iml调用bind方法。

  /**   *   * Binds the {@code ServerSocket} to a specific address   * (IP address and port number).   * <p>   * If the address is {@code null}, then the system will pick up   * an ephemeral port and a valid local address to bind the socket.   * <P>   * The {@code backlog} argument is the requested maximum number of   * pending connections on the socket. Its exact semantics are implementation   * specific. In particular, an implementation may impose a maximum length   * or may choose to ignore the parameter altogther. The value provided   * should be greater than {@code 0}. If it is less than or equal to   * {@code 0}, then an implementation specific default will be used.   * @param  endpoint    The IP address and port number to bind to.   * @param  backlog     requested maximum length of the queue of   *             incoming connections.   * @throws IOException if the bind operation fails, or if the socket   *           is already bound.   * @throws SecurityException    if a {@code SecurityManager} is present and   * its {@code checkListen} method doesn't allow the operation.   * @throws IllegalArgumentException if endpoint is a   *     SocketAddress subclass not supported by this socket   * @since 1.4   */  public void bind(SocketAddress endpoint, int backlog) throws IOException {    if (isClosed())      throw new SocketException("Socket is closed");    if (!oldImpl && isBound())      throw new SocketException("Already bound");    if (endpoint == null)      endpoint = new InetSocketAddress(0);    if (!(endpoint instanceof InetSocketAddress))      throw new IllegalArgumentException("Unsupported address type");    InetSocketAddress epoint = (InetSocketAddress) endpoint;    if (epoint.isUnresolved())      throw new SocketException("Unresolved address");    if (backlog < 1)     backlog = 50;    try {      SecurityManager security = System.getSecurityManager();      if (security != null)        security.checkListen(epoint.getPort());      getImpl().bind(epoint.getAddress(), epoint.getPort());      getImpl().listen(backlog);      bound = true;    } catch(SecurityException e) {      bound = false;      throw e;    } catch(IOException e) {      bound = false;      throw e;    }  }

  方法:public Socket accept() throws IOException

     监听连接,连接成功后创建一个Socket。在连接传入之前会一直阻塞.这个方法最后会调implAccept(Socket s) 

  /**   * Listens for a connection to be made to this socket and accepts   * it. The method blocks until a connection is made.   *   * <p>A new Socket {@code s} is created and, if there   * is a security manager,   * the security manager's {@code checkAccept} method is called   * with {@code s.getInetAddress().getHostAddress()} and   * {@code s.getPort()}   * as its arguments to ensure the operation is allowed.   * This could result in a SecurityException.   *   * @exception IOException if an I/O error occurs when waiting for a   *        connection.   * @exception SecurityException if a security manager exists and its   *       {@code checkAccept} method doesn't allow the operation.   * @exception SocketTimeoutException if a timeout was previously set with setSoTimeout and   *       the timeout has been reached.   * @exception java.nio.channels.IllegalBlockingModeException   *       if this socket has an associated channel, the channel is in   *       non-blocking mode, and there is no connection ready to be   *       accepted   *   * @return the new Socket   * @see SecurityManager#checkAccept   * @revised 1.4   * @spec JSR-51   */  public Socket accept() throws IOException {    if (isClosed())      throw new SocketException("Socket is closed");    if (!isBound())      throw new SocketException("Socket is not bound yet");    Socket s = new Socket((SocketImpl) null);    implAccept(s);    return s;  }

  方法:protected final void implAccept(Socket s) throws IOException

 /**   * Subclasses of ServerSocket use this method to override accept()   * to return their own subclass of socket. So a FooServerSocket   * will typically hand this method an <i>empty</i> FooSocket. On   * return from implAccept the FooSocket will be connected to a client.   *   * @param s the Socket   * @throws java.nio.channels.IllegalBlockingModeException   *     if this socket has an associated channel,   *     and the channel is in non-blocking mode   * @throws IOException if an I/O error occurs when waiting   * for a connection.   * @since  JDK1.1   * @revised 1.4   * @spec JSR-51   */  protected final void implAccept(Socket s) throws IOException {    SocketImpl si = null;    try {      if (s.impl == null)       s.setImpl();      else {        s.impl.reset();      }      si = s.impl;      s.impl = null;      si.address = new InetAddress();      si.fd = new FileDescriptor();      getImpl().accept(si);      SecurityManager security = System.getSecurityManager();      if (security != null) {        security.checkAccept(si.getInetAddress().getHostAddress(),                   si.getPort());      }    } catch (IOException e) {      if (si != null)        si.reset();      s.impl = si;      throw e;    } catch (SecurityException e) {      if (si != null)        si.reset();      s.impl = si;      throw e;    }    s.impl = si;    s.postAccept();  }

  • Socket 例子 

   1.编写服务端代码:

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class Server {    private static ServerSocket server;  public static void main(String args[]) throws IOException {    server = new ServerSocket(9000);    Socket socket = null;    BufferedReader in = null;    PrintWriter out = null;        while(true){      try {        socket = server.accept();            in =new BufferedReader( new InputStreamReader(socket.getInputStream()));      out = new PrintWriter(socket.getOutputStream(),true);      if(in.ready()){        System.out.println("recive data from client : "+in.readLine());      }      out.print("welcome to server.");      } catch (IOException e) {        // TODO Auto-generated catch block        e.printStackTrace();      }finally{        out.close();        in.close();      }          }          }}

  2.编写客户端:

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class Client {  static Socket client =null;  public static void main(String args[]){        BufferedReader in=null;    PrintWriter out=null;    try {       client = new Socket("127.0.0.1",9000);       in = new BufferedReader(new InputStreamReader(client.getInputStream()));       out= new PrintWriter(client.getOutputStream(),true);       out.println("hello server.");       System.out.println("recive data from server "+in.readLine());    } catch (IOException e) {      // TODO Auto-generated catch block      e.printStackTrace();    }finally{      out.close();      try {        in.close();      } catch (IOException e) {        // TODO Auto-generated catch block        e.printStackTrace();      }    }      }}

  运行结果:

  server: recive data from client : hello server.

     client:recive data from server welcome to server.