Java Socket 入门
- 什么是Socket?
API说:他是套接字,两台机器连接的端点。他的实际工作由SocketIml执行。
没图说个**,下面用个图能说明Socket,在哪个位置:
- 相关源码:
1.Socket:
属性:SocketImpl impl,socket的实现,就是API说的,实际工作由它执行。
public class 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.