Socket
Socket实际上就是IP +协议+port
位于应用层和传输层的一个抽象层
TCP中的socket
方法介绍
客户端
- 构造函数
Socket socket = new Socket(Proxy.NO_PROXY);等于无参构造
带代理构造
Proxy proxy = new Proxy(Proxy.Type.HTTP/DIREC/SOCKS, new InetSocketAddress(Inet4Address.getByName("“www.baidu.com”),prot:8800));
Socket socket = new Socket(proxy)
Socket socket = new Socket(本地PORT//Inet4Address.getLocalHost(),PORT);//建立socket连接本地的PORT;
Socket socket = new Socket(远程的host,远程的port,本地的host,本地的端口);
Socket socket = new Socket();
socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(),LOCAL_PORT));
socket.connect(); 上面一行等于这个三行 有bind 有 connect
- 初始化
设置读取超时时间
socket.setSoTimeout
是否复用未完成关闭的Socket地址 对于bind后的套接字有效
socket.setReuseAddress(true); 一般连接结束后 两分钟内不能再次使用
socket.setTcpNoDelay(true);//是否开启Nagle算法
socket.setKeepAlive(true); //长时间无数据响应发送确认数据,如果没有收到回送则认为连接中断,会抛出异常
socket.setSoLinger(true,200);//默认情况是false,0 调用close关闭,底层系统接管输出流,将缓冲区域发送完成
设置为true,0 关闭立即返回 缓冲区的数据直接抛弃 直接发送RST结束命令 无需经过2MSL时间等待
true,200 关闭最长阻塞时间为200ml 随后按第二种情况处理
socket.setOOBInline(false); 是否让紧急数据接收,不建议开启会和行为数据混杂
setSendBufferSize
setReceiveBufferSize 缓冲区大小 如果小于这个值就直接发 不然就拆分
setPerformancePreferences(短连接,延迟,带宽)的权重 - 连接操作
socket.connect(new InetSocketAddress(IP,PORT))
服务端
- 创建
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocket(Inet4Address.getLocalHost(),PORT),50);
允许等待的队列为50个 超过50个会在客户端超过异常
同样建议使用空的构造函数在初始化之后进行绑定 - 初始化
setReuseAddress
setReceiveBufferSize
setSoTimeout//设置accept 的超时时间
setPerformancePreferences
一些重要方法
- listen()函数
int listen(SOCKET sock, int backlog); //Windows
listen() 函数的主要作用就是将 socket() 函数得到的 sockfd 从 主动连接变成一个被动监听的套接字, 用来被动等待客户端的连接, 而参数 backlog 的作用就是设置连接队列的长度 - 如果有客户端通过 connect() 发起连接请求, 内核就会通过三次握手建立连接, 然后将建立好的连接放到已完成连接队列
通常客户端通过 connect() 函数来向服务端主动发起连接, 通知内核通过三次握手建立连接, 然后将结果返回给这个函数. 这个函数默认会一直阻塞, 直到内核连接建立成功或者超时失败才返回(但一般这个过程很快)
服务器端通过 listen() 函数来通知内核建立连接,
客户端通过 connect() 函数来通知内核建立连接
如果在 listen 之后不进行 accept , connect 也是会成功返回的, 其实此时连接就已经建立好了 - accept() 函数的作用就是在已完成连接队列中取出一个已经建立好的连接
如果这个队列中已经没有已完成连接的套接字, 那么 accept() 就会一直阻塞, 直到取得一个已经建立连接的套接字 - shutdown()
调用 close()/closesocket() 函数意味着完全断开连接,即不能发送数据也不能接收数据,这种“生硬”的方式有时候会显得不太“优雅”。
int shutdown(SOCKET s, int howto);
SD_RECEIVE:关闭接收操作,也就是断开输入流。
SD_SEND:关闭发送操作,也就是断开输出流。
SD_BOTH:同时关闭接收和发送操作。
close()和shutdown()的区别
确切地说,close()用来关闭套接字,之后再也不能使用该套接字,与C语言中的 fclose() 类似。应用程序关闭套接字后,与该套接字相关的连接和缓存也失去了意义,TCP协议会自动触发关闭连接的操作。
shutdown() 用来关闭连接,直到调用 close() 将套接字从内存清除。
调用 close()/closesocket() 关闭套接字时,或调用 shutdown() 关闭输出流时,都会向对方发送 FIN 包。FIN 包表示数据传输完毕,计算机收到 FIN 包就知道不会再有数据传送过来了。
默认情况下,close()会立即向网络中发送FIN包,不管输出缓冲区中是否还有数据,而shutdown() 会等输出缓冲区中的数据传输完毕再发送FIN包。也就意味着,调用 close() 将丢失输出缓冲区中的数据,而调用 shutdown() 不会。 - send() recv()
int send(SOCKET sock, const char *buf, int len, int flags);
int recv(SOCKET sock, const char *buf, int len, int flags);
sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数
UDP的socket
使用DatagramSocket 和 Datagrampacket
发送端
- 建立DatagramSocket服务;
- 提供数据,并把数据封装到字节数组中
- 创建DatagramPacket数据包,将数据封装到包中,同时指定IP和接收端口
- 通过Socket服务,利用send方法将数据包发出去
- 关闭DatagramSocket 和 Datagrampacket
接收端
- 建立DatagramSocket服务 并且监听一个端口;
- 定义一个Byte[]字节数组 和一个Datagrampacket数据包,并把数组封装进数据包
- 调用DatagramPacket的receive的方法,将接收数据存入到包中
- 关闭DatagramSocket 和 Datagrampacket
具体方法
-
DatagramSocket相关的方法
DatagramSocket()
构造数据包的套接字并绑定本地主机上任何可用的端口
DatagramSocket(int port)
绑定指定的端口
DatagramSocket(int port, IntetAddress addr)
绑定到本地地址
close()
关闭套接字
InetAddress getInetAddress()
返回与套接字连接的地址
InetAddress getLocalAddress()
获取绑定的本地地址
int getPort()
返回套接字的端口
void receive(DatagramPacket p)
从套接字接收数据报
void sendDatagramPacket p)
从套接字发送数据报 -
DatagramPacket的方法
DatagramPacket(byte[] buf,int length)
构造DatagramPacket,用来接收长度为length的数据报
DatagramPacket(byte[] buf,int length,int port, IntetAddress addr)
用来将长度为length的数据包发送到指定主机的指定端口号上
InetAddress getAddress()
返回某台机器的IP地址
byte() getData()
返回数据缓冲区
int getLength()
返回数据的长度
int getPort()
返回远程主机的端口号
setData(byte[] buf, int length ,int offset)
setLength
粘包问题
例如,write()/send() 重复执行三次,每次都发送字符串"abc",那么目标机器上的 read()/recv() 可能分三次接收,每次都接收"abc";也可能分两次接收,第一次接收"abcab",第二次接收"cabc";也可能一次就接收到字符串"abcabcabc"。
这就是数据的“粘包”问题,客户端发送的多个数据包被当做一个数据包接收。也称数据的无边界性,read()/recv() 函数不知道数据包的开始或结束标志(实际上也没有任何开始或结束标志),只把它们当做连续的数据流来处理。