Java Socket底层采用TCP/IP协议通信,通信细节被封装,我们仅仅需要指定IP、端口,便能轻易地创建TCP或UDP连接,进行网络通信。
TCP 与 UDP
TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。
通过TCP协议传输,得到的是一个顺序的无差错的数据流。
发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,
当一个socket(通常都是serverSocket)等待建立连接时,另一个socket可以要求进行连接,
一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
UDP是User Datagram Protocol的简称,是一种无连接的协议.
每个数据报都是一个独立的信息,包括完整的源地址或目的地址。
它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
比较:
TCP:1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接时间。
2,TCP传输数据没有大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的数据。
3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
UDP:1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
2,UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
通知服务端命令发送完的方式
客户端打开一个输出流,如果不做约定,也不关闭它,那么服务端永远不知道客户端是否发送完消息。
服务端会一直等待下去,直到读取超时。
1. 客户端关闭Socket
如果客户端想再次发送消息或接收消息,需要重现创建Socket连接
2. 客户端关闭Socket输出流:socket.shutdownOutput();
调用Socket的shutdownOutput()方法,底层会告知服务端我这边已经写完了,
那么服务端收到消息后,就能知道已经读取完消息
如果服务端有要返回给客户的消息那么就可以通过服务端的输出流发送给客户端。
如果没有,直接关闭Socket。
再次发送,需要重新建立Socket连接
3. 约定符号(结尾发送一个字符串标记标识发送完毕,客户端与服务端需要统一标识)
不需要关闭流,当发送完一条命令(消息)后可以再次发送新的命令(消息)
4. 通过指定长度
1个字节:最大256,表示256B
2个字节:最大65536,表示64K
3个字节:最大16777216,表示16M
4个字节:最大4294967296,表示4G
客户端(2个字节表示长度):
OutputStream outputStream = socket.getOutputStream();
byte[] sendBytes = "消息".getBytes("UTF-8");
/**
* 将消息的长度优先发送出去
* >> 有符号右移位,将运算数的二进制整体右移指定位数,整数高位用0补齐,负数高位用1补齐
*/
//首先需要计算得知消息的长度
//int类型高8位(一个字节)
outputStream.write(sendBytes.length >>8);
//int类型低八位(将指定的字节写入此输出流:只有低八位有效,高位会被忽略)
outputStream.write(sendBytes.length);
//然后将消息再次发送出去
outputStream.write(sendBytes);
outputStream.flush();
服务端(2个字节表示长度):
InputStream inputStream = socket.getInputStream();
byte[] bytes;
while (true) {
//首先读取两个字节表示的长度
int first = inputStream.read();
//如果读取的值为-1 说明到了流的末尾,Socket已经被关闭了,此时将不能再去读取
if(first==-1){
break;
}
int second = inputStream.read();
int length = (first << 8) + second;
//然后构造一个指定长的byte数组
bytes = new byte[length];
//然后读取指定长度的消息即可
inputStream.read(bytes);
System.out.println(new String(bytes, "UTF-8"));
}