客户端Socket
1、什么是客户端Socket?
客户端Socket相当于一个可以读写的字节流。
2、使用Socket,Socket是两台主机之间的一个连接
a) 连接远程主机
b) 发送数据
c) 接收数据
d) 关闭连接
e) 绑定端口
f) 监听入站数据
g) 在绑定端口上接收来自远程机器(客户端)的连接。
Java的socket类(客户端和服务器都可以使用)提供了对应前4个操作的方法。后面3个操作仅服务器需要,即等待客户端的连接。Java程序通常采用以下方式使用客户端Socket:
程序用构造函数创建一个新的Socket。
Socket尝试连接远程服务器。
3、从服务器读取数据
创建一个连接:Socket socket = new Socket(url,port);
通过socket.getInputStream获取Inpustream对象,此时可以通过API读取服务器所送的数据。
对于大多数类似这样的网络程序中,重点工作通常是使用协议和理解协议,从而对socket所传输的数据进行解析。
4、向服务器写数据
同样是创建一个连接:Socket socket = new Socket(ip,port);
通过socket.getOutputStream获取Outpustream对象,此时可以通过API向服务器发送数据。
5、半关闭连接
close()方法会同时关闭socket的输入和输出。有时你可能希望只关闭连接的一般即输入或者输出。shutdownInput()和shutdownOutput()方法只关闭连接的一半。但这并不会关闭socket。实际上它会调整与socket连接的流,使它认为已经到了流的末端。关闭输入之后再读取会返回-1,关闭输出之后再写入socket会抛出IOexception。注意,即使半关闭了连接,或将连接的两半都关闭,使用结束后仍需要关闭改socket。shutdown方法只会影响socket流,并不释放与socket相关的资源和所占用的端口。可以用过isInputStream()和isOutputStream()方法确定输入和输出流是否打开或者关闭。
6、构造和连接Socket
Socket(String host,int port);
Socket(InetAdress host, int port);
7、构造但不连接Socket
Socket();
Socket.connect(socketAdress)可以通过传入一个SocketAddress对象来穿件一个连接的socket;之所以有这个构造函数,是为了支持不同类型的socket,还需要用它设置一个socket选项,这个选项只能在socket连接之前改变。而SocketAdress主要用途是为暂时的socket连接信息(IP地址和端口)提供一个方便的存储,即使最初的socket已断开并被垃圾回收,这些信息也可以重新用来创建新的socket。
8、代理服务器
Socket(proxy);
一般情况下,socket使用的代理服务器由socksProxyHost和socksProxyPort系统属性控制,这些属性应用于系统中的所有socket。
下面代码使用位于ip的socks代理服务器来连接主机remoteip;
SocketAddress proxyAddress = new InetSocketAddress(ip, port);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress ) ;
Socket s = new Socket(proxy);
SocketAddress remote = new InetSocketAddress(remoteip, port);
s.connect(remote);
9、获取Socket信息
getInetAddress();//获取远程地址
getPort();//获取远程地址端口
getLocalAddress();//获取本地地址
getLocalPort();//本地端口
由于socket是临时对象,一般来讲,socket的持续时间与他们表示的连接是一直的,所以没有理由把它们存储在散列表中或者进行比较。因此socket没有覆盖equals()或者hashCode()方法,这些方法的语义完全等同于Object宗相应方法。当且仅当两个socket对象是同一个对象时他们才相等。
10、设置Socket选项
a) TCP_NODEAY:设置TCP_NODEAY为true会确保包会尽快地发送,而无论包大小。正常情况下,小数据包(一字节)在发送前会组合为更大的包。
b) SO_LINGER:该选项制定了socket关闭时如何处理尚未发送的数据。默认情况下,close()方法将立即返回,但系统仍会尝试发送剩余的数据。如果延迟时间设置为0,那么当socket关闭时,所有未发送的数据包都将被丢弃。如果该选项打开并且延迟时间设置为任意正整数,close()方法将会被阻塞(阻塞时间为指定秒数),等待发送数据和确认接收。当过去相应秒数时,所有剩余数据都不会被发送,也不会收到确认。
c) SO_TIMETOUT:设置超时时间
d) SO_RCVBUF和SO_SNDBUF:设置接收和发送缓冲区
e) SO_KEEPALIVE:如果打开了SO_KEEPALIVE,客户端偶尔会通过一个空闲连接发送一个数据包(一般两小时一次),以确保服务器为崩溃。
f) OOBINLINE:TCP包括一个可以发送单字节之外“紧急数据”。这个数据会立即发送。此时接收方收到紧急数据时会得到通知,在处理其他已收到的数据之前可以选择先处理这个数据。
g) SO_REUSEADDR:一个socket关闭时,可能不会立即释放本地端口,尤其是当socket关闭时仍有一个打开的连接,就不会释放本地端口。有时会等待一小段时间,确保接收到所有要发送到这个端口的延迟数据包,socket关闭时这些数据包可能人在网络上传输。系统不会对接收到的延迟包做任何处理,只是希望确保这些数据不会意外地传入到绑定到统一端口的新进程。
如果使用随机端口,不会存在大问题,但是如果socket绑定到一个已知端口,就会阻塞所有其他socket同时使用这个端口。如果SO_REUSEADDR(默认关闭),就允许另一个socket绑定到这个端口,即使此时仍有可能存在前一个socket未接收的数据。
在java中,可以通过以下两个方法来控制:
void setReuseAddress(boolean on);
boolean getReuseAddress();
要正常使用这些放法,setReuseAddress()必须在为这个端口绑定新端口之前调用。这意味着socket必须使用无参构造函数以非连接状态创建,然后调用setReuseAddress(true),再使用connect()方法连接socket。之前连接的socket和重用的老地址的新socket都必须设置setReuseAddress为true才能生效。
11、Socket异常
BindException 如果在正在使用的端口上构造socket或serverSocket对象,或者没有足够的权限使用这个端口,就会抛出该异常。
ConnectException 当远程主机拒绝,而拒绝的原有通常是主机忙或者没有进程监听该端口,就会抛出该异常。
NoRouteToHostException:连接超时
ProtocalException:网络接收数据违反TCP/IP规范。