余一

纸上得来终觉浅,绝知此事要躬行。

0%

UDP与TCP入门

报文、协议、Mac地址

1.报文段

  • 报文段是指TCP/IP协议网络传输中,起着路由导航作用
  • 用以查询各个网络路由网段、IP地址、交换协议等IP数据包
  • 报文段充当整个TCP/IP协议数据包的导航路由功能
  • 报文段在传输过程中会不断的封装成组、包、帧来传输
  • 封装方式就是添加一些控制信息组成的首部,即报文头

2.传输协议

  • 一种规定,约束
  • 简单来说:A > B的传输数据,B能识别,反之B > A的传输数据A也能识别。这就是协议

3.Mac地址

  • Media Access Control或者Medium Access Control
  • 译为媒体访问控制,或称物理地址、硬件地址
  • 用来定义网络设备的位置
  • 形如:44-45-53-54-00-00 与身份证类似

    IP、端口以及远程服务器

  1. IP地址

    • 互联网协议地址(Internet Protocol Address)

    • 是 分配给网络上使用网际协议(Internet Protocol ,IP)的设备数字 标签

    • 常见IP地址分为IPV4IPV6

    • IP地址由32位二进制数组成,常以xxx.xxx.xxx.xxx形式表现,每组xxx代表小于或等于255的10进制数

      IPV4:

      • 如:208.80.152.2
      • 分为A、B、C、D、E五大类,其中E类属于特殊保留地址
      • 总数量:42亿个,最终于2011年2月2日用尽
      • 如果主机号全是1,那么这个地址为直接广播地址
      • IP地址”255.255.255.255”为受限广播地址

      IPV6:

      • 总共有128位长,IPV6地址的表达形式,一般采用32个十六位进制数。也可以想象为1632个
      • 由两个逻辑部分组成:一个64位的网络前缀和一个64位的主机地址,主机地址通常根据物理地址自动生成,叫做EUI-64(或者64位扩展唯一标识)
      • 2001:0db8:85a3:0000:1319:8a2e:0370:7344
      • IPV4转换为IPV6一定可行,IPV6转换为IPV4不一定可行
  2. 端口

    • 如果把IP地址比作一间房子,端口就是出入这间房子的门或窗户
    • 不同门窗户后有不同的人(代表着不同程序的进程),房子中的用户与外界交流的出口
    • 外界鸽子(信息)飞到不同窗户也就是给不同的人(程序进程)传递信息
    • 从0到1023号端口以及1024到49151号端口都是特殊端口
    • 计算机之间依照互联网传输层TCP/IP协议的协议通信,不同协议对应不同端口
    • 49152到65535号端口属于”动态端口”范围,没有端口可以被正式的注册占用

数据传输层次:

3.远程服务器:

  • 局域网:一般而言,家里的环境以及公司相互电脑之间环境都属于局域网
  • 我与你们之间的电脑属于互联网,而非局域网
  • 默认的:我的电脑无法直接链接到你们的电脑:不在同一局域网,可通过远程服务器通信

Socket UDP

UDP是什么

  • User Datagram Protocol
  • 用户数据报协议,又称用户数据报文协议
  • 是一个简单的面向数据报传输层协议,正式规范为RFC 768
  • 用户数据协议,非连接协议
为什么不可靠
  1. 一旦把应用程序发给网络层的数据发送出去,就不保留数据备份
  2. UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)
  3. 发送端产生数据,接收端从网络中抓取数据
  4. 结构简单,无校验,速度快,容易丢包,可广播
UDP能做什么
  • DNS,TFTP,SNMP
  • 视频,音频,普通数据(无关紧要数据)

UDP包最大长度
  • 16位 -> 2字节 存储长度信息
  • 2^16 -1 = 64k -1 = 65536 -1 = 65535
  • 自身协议占用:32 + 32位 = 64位 = 8字节,而在IP层进行封装后的IP包头占去20字节。
  • 65535 - 8 - 20 = 65507 byte

UDP核心API

API-DatagramSocket
  • 用于接收与发送UDP的类
  • 负责发送某一个UDP包,或者接收UDP包
  • 不同于TCP,UDP并没有合并到Socket API中
  • DatagramSocket()创建简单实例,不指定端口与IP
  • DatagramSocket(int port)创建监听某端口的实例
  • DatagramSocket(int port,InetAddress localAddr)创建固定端口指定IP的实例
  • receive(DatagramPacket d) : 接收
  • send(DatagramPacket d) :发送
  • setSoTimeout(int timeout):设置超时,毫秒
  • close()关闭,释放资源
API-DatagramPacket
  • 用于处理报文
  • 将byte数组、目标地址、目标端口等数据包封装成报文或者将报文拆卸成byte数组
  • 是UDP的发送实体,也是UDP的接收实体
  • DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port):
  • 前面3个参数指定buff的使用区间
  • 后面2个参数指定目标机器地址,与端口(发送端地址,端口
  • DatagramPacket(byte[] buf,int length,SocketAddress address)
  • 前面2个参数指定buff的使用区间
  • SocketAddress 相当于InetAddress + port
  • setData(byte[] buf,int offset,int length)
  • setData(byte[] buf)
  • setLength(int length)
  • getData()、getOffset()、getLength()
  • setAddress(InetAddress iaddr)、setPort(int port)
  • getAddress()、getPort()
  • setSocketAddress(SocketAddress address)
  • getSocketAddress()

UDP单播、广播、多播

单播:1对1

多播(组播):1对多,一次给一个组发

广播:给所有设备发送

IP地址类别

广播地址
  • 255.255.255.255 为受限广播地址
  • C网广播地址一般为:xxx.xxx.xxx.255(192.168.1.255)
  • D类IP地址为多播预留
IP地址构成

广播地址运算

比如:

根据子网掩码:

  • 255.255.255.192 -> 11111111.11111111.11111111.11000000
  • 可划分网段:最后八位2个1,即:2^2 = 4 个
  • 063、64127、128191、192255
  • 由于例子IP在第一个网段,所以广播地址:192.168.124.63
广播通信问题

如图,主机一发送广播后主机二不能收到,因为不在同一个网段,广播地址不一样。

局域网搜索案例

  1. UDP接收消息并回送功能实现

    UDP接收者:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    /**
    * @author ljh
    * @ClassName UDPProvider.java
    * @createTime 2022年08月03日
    * UDP服务提供者,用于提供服务
    */
    public class UDPProvider {
    public static void main(String[] args) throws IOException {
    System.out.println("UDPProvider started...");

    //作为接收者,接收指定为20000端口的数据
    DatagramSocket datagramSocket = new DatagramSocket(20000);

    //构建接收实体
    final byte[] buf = new byte[512];
    DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

    //接收
    datagramSocket.receive(receivePacket);

    //打印接收到的信息与发送者的信息
    //发送者的IP地址
    String ip = receivePacket.getAddress().getHostAddress();
    int port = receivePacket.getPort();
    int dataLength = receivePacket.getLength();
    String receiveDate = new String(receivePacket.getData(), 0, dataLength);
    System.out.println("UDPProvider receive from ip:"+ip+"\tport:"+port+"\tdata:"+receiveDate);

    //构建一份回送数据
    String responseData = "Receive data with len :" + dataLength;
    byte[] receiveDateBytes = responseData.getBytes();
    //直接根据发送者构建一份回送信息
    DatagramPacket responsePacket = new DatagramPacket(receiveDateBytes,
    receiveDateBytes.length,
    receivePacket.getAddress(),
    receivePacket.getPort());

    datagramSocket.send(responsePacket);

    System.out.println("UDPProvider finished...");
    datagramSocket.close();
    }
    }

    UDP搜索者:搜索提供方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    /**
    * @author ljh
    * @ClassName UDPSearcher.java
    * @createTime 2022年08月03日
    * UDP搜索者,用于搜索服务支持方
    */
    public class UDPSearcher {
    public static void main(String[] args) throws IOException {
    System.out.println("UDPSearcher started...");

    //作为搜索方,让系统自动分配端口
    DatagramSocket datagramSocket = new DatagramSocket();

    //构建一份请求数据
    String requestData = "Only the dead do not make mistakes.";
    byte[] requestDateBytes = requestData.getBytes();
    //直接根据发送者构建一份回送信息
    DatagramPacket requestPacket = new DatagramPacket(requestDateBytes,
    requestDateBytes.length);
    //发送端的Ip
    requestPacket.setAddress(InetAddress.getLocalHost());
    //目标端口
    requestPacket.setPort(20000);
    //发送
    datagramSocket.send(requestPacket);

    //构建接收实体
    final byte[] buf = new byte[512];
    DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

    //接收
    datagramSocket.receive(receivePacket);

    //打印接收到的信息与发送者的信息
    //发送者的IP地址
    String ip = receivePacket.getAddress().getHostAddress();
    int port = receivePacket.getPort();
    int dataLength = receivePacket.getLength();
    String receiveDate = new String(receivePacket.getData(), 0, dataLength);
    System.out.println("UDPSearcher receive from ip:"+ip+"\tport:"+port+"\tdata:"+receiveDate);


    System.out.println("UDPSearcher finished...");
    datagramSocket.close();
    }
    }

  2. UDP局域网广播发送实现

  3. UDP局域网回送消息实现

己方,监听20000端口,并回送指定30000端口数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* @author ljh
* @ClassName UDPProvider.java
* @createTime
* UDP提供者,用于提供服务,监听端口信息,然后回送
*/
public class UDPProvider {
public static void main(String[] args) throws IOException {
//生成一份唯一标识
String sn = UUID.randomUUID().toString();
Provider provider = new Provider(sn);
provider.start();

//读取任意键信息后可以退出
System.in.read();
provider.exit();
}

private static class Provider extends Thread{
private final String sn;
private boolean done = false;
private DatagramSocket datagramSocket = null;

public Provider(String sn){
super();
this.sn = sn;
}

@Override
public void run() {
super.run();
System.out.println("UDPProvider started...");

try {
//作为接收者,接收指定为20000端口的数据
datagramSocket = new DatagramSocket(20000);
while (!done){
//构建接收实体
final byte[] buf = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

//接收对方暗号
datagramSocket.receive(receivePacket);

//打印接收到的信息与发送者的信息
//发送者的IP地址
String ip = receivePacket.getAddress().getHostAddress();
int port = receivePacket.getPort();
int dataLength = receivePacket.getLength();
String receiveData = new String(receivePacket.getData(), 0, dataLength);
System.out.println("UDPProvider receive from ip:"+ip+"\tport:"+port+"\tdata:"+receiveData);

//对方端口
int responsePort = MessageCreator.parsePort(receiveData);
if (responsePort != -1){
//构建一份回送数据
String responseData = MessageCreator.buildWithSn(sn);
byte[] receiveDateBytes = responseData.getBytes();
//构建一份回送信息
DatagramPacket responsePacket = new DatagramPacket(receiveDateBytes,
receiveDateBytes.length,
receivePacket.getAddress(),
responsePort);

datagramSocket.send(responsePacket);
}
}
}catch (IOException e){
e.printStackTrace();
}finally {
close();
}
System.out.println("UDPProvider finished...");
}

private void close(){
if (datagramSocket != null){
datagramSocket.close();
datagramSocket = null;
}
}

void exit(){
done = true;
close();
}
}
}

搜索端,发送20000端口数据暗号,监听30000端口回复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* @author ljh
* @ClassName UDPSearcher.java
* @createTime 2022年08月03日
* UDP搜索者,用于搜索服务支持方
*/
public class UDPSearcher {

private static final int LISTEN_PORT = 30000;

public static void main(String[] args) throws IOException, InterruptedException {
//做好监听3000端口的准备,并显示对方回复数据
Listener listen = listen();
//给所有监听20000端口的端发送广播,告诉他们给30000端口回复数据
sendBroadcast();

//读取任意键盘信息退出
System.in.read();
listen.getDevicesAndClose();

}

private static Listener listen() throws InterruptedException {
System.out.println("UDPSearcher start listen...");
CountDownLatch countDownLatch = new CountDownLatch(1);
Listener listener = new Listener(LISTEN_PORT, countDownLatch);
listener.start();
countDownLatch.await();
return listener;
}

/**
* 发送广播
*/
private static void sendBroadcast() throws IOException {
System.out.println("UDPSearcher started...");

//作为搜索方,让系统自动分配端口
DatagramSocket datagramSocket = new DatagramSocket();

//构建一份请求数据
String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
byte[] requestDateBytes = requestData.getBytes();
//直接根据发送者构建一份回送信息
DatagramPacket requestPacket = new DatagramPacket(requestDateBytes,
requestDateBytes.length);
//广播地址
requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
//发送的目标端口
requestPacket.setPort(20000);
//发送
datagramSocket.send(requestPacket);
datagramSocket.close();
//完成
System.out.println("UDPSearcher finished...");
}

private static class Device{
final int port;
final String ip;
final String sn;

public Device(int port, String ip, String sn) {
this.port = port;
this.ip = ip;
this.sn = sn;
}

@Override
public String toString() {
return "Device{" +
"port=" + port +
", ip='" + ip + '\'' +
", sn='" + sn + '\'' +
'}';
}
}

private static class Listener extends Thread{

private final int listenPort;
private final CountDownLatch countDownLatch;
private final List<Device> devices = new ArrayList<>();
private boolean done = false;
private DatagramSocket ds;

public Listener(int listenPort,CountDownLatch countDownLatch){
super();
this.listenPort = listenPort;
this.countDownLatch = countDownLatch;
}


@Override
public void run() {
super.run();
//通知已经启动
countDownLatch.countDown();
try {
ds = new DatagramSocket(listenPort);
while (!done){
//构建接收体
//构建接收实体
final byte[] buf = new byte[512];
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

//接收
ds.receive(receivePacket);

//打印接收到的信息与发送者的信息
//发送者的IP地址
String ip = receivePacket.getAddress().getHostAddress();
int port = receivePacket.getPort();
int dataLength = receivePacket.getLength();
String receiveData = new String(receivePacket.getData(), 0, dataLength);
System.out.println("UDPSearcher receive from ip:"+ip+"\tport:"+port+"\tdata:"+receiveData);

String sn = MessageCreator.parseSn(receiveData);
if (sn!=null){
Device device = new Device(port, ip, sn);
devices.add(device);
}
}

}catch (Exception e){

}finally {
close();
}
System.out.println("UDPSearcher listener finished.");
}

private void close(){
if (ds != null){
ds.close();
ds = null;
}
}

void exit(){
done = true;
close();
}
List<Device> getDevicesAndClose() {
done = true;
close();
return devices;
}
}
}

约定一个通信方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* @author ljh
* @ClassName MessageCreater.java
* @createTime 2022年08月04日
*/
public class MessageCreator {
private static final String SN_HEADER = "收到暗号,我是(SN):";
private static final String PORT_HEADER = "这是暗号,请回电端口(port):";

/**
* 构建发送暗号
* @param port
* @return
*/
public static String buildWithPort(int port){
return PORT_HEADER + port;
}

/**
* 解析收到暗号信息,获取对方端口
* @param data
* @return
*/
public static int parsePort(String data){
if (data.startsWith(PORT_HEADER)){
return Integer.parseInt(data.substring(PORT_HEADER.length()));
}
return -1;
}

/**
* 构建回复SN暗号
* @param sn
* @return
*/
public static String buildWithSn(String sn){
return SN_HEADER + sn;
}

/**
* 解析对方回复SN暗号
* @param data
* @return
*/
public static String parseSn(String data){
if (data.startsWith(SN_HEADER)){
return data.substring(SN_HEADER.length());
}
return null;
}
}

Socket TCP

TCP是什么

  • Transmission Control Protocol(TCP)
  • TCP是传输控制协议,是一种面向连接、可靠、基于字节流的传输层通信协议,由IETF的RFC 793定义

TCP机制

  • 三次握手、四次挥手
  • 具有校验机制、可靠、数据传输稳定

TCP能做什么

  • 聊天消息传输、推送
  • 单人语音、视频聊天等
  • 几乎所有UDP能做到的事都能做,但需要考虑复杂性、性能问题
  • 无法进行广播、多播等操作

TCP核心API

  • socket():创建一个Socket
  • bind():绑定一个Socket到一个本地地址和端口上
  • connect():连接到远程套接字
  • accept():接受一个新的连接
  • write():把数据写入到Socket输出流
  • read():从socket输入流读取数据

TCP连接可靠性-三次握手

​ 比如打电话:客户端打给服务端说:可以听到我说话吗,然后服务端回复:能听到你说话,你能听到我说话吗,客户端再回复:可以听到。这时,三次握手结束,它们就建立了连接。

TCP四次挥手

​ 首先:发送端向接收端说:我想和你断开连接了,接收端回复说OK你可以断开对我的连接了;这时发送端的输出操作可以断开了,读取操作还保留着,服务端会把还没送答的消息一一送达给接收端,送达完成后,接收端会跟发送端说:他想要关闭连接了。发送端回复说:Ok,你可以断开连接了。

传输可靠性

  • 分片发送,排序,顺序发送,顺序组装
  • 丢弃、超时
  • 重发机制-定时器

案例实操

TCP传输初始化配置/传输数据

Tools.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Tools {
public static int byteArrayToInt(byte[] b) {
return b[3] & 0xFF |
(b[2] & 0xFF) << 8 |
(b[1] & 0xFF) << 16 |
(b[0] & 0xFF) << 24;
}

public static byte[] intToByteArray(int a) {
return new byte[]{
(byte) ((a >> 24) & 0xFF),
(byte) ((a >> 16) & 0xFF),
(byte) ((a >> 8) & 0xFF),
(byte) (a & 0xFF)
};
}
}

Client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
public class Client {
//服务器远程端口
private static final int PORT = 20000;
//当前进程端口
private static final int LOCAL_PORT = 20001;

public static void main(String[] args) throws IOException {
//创建clientSocket
Socket socket = createSocket();

//初始化
initSocket(socket);

// 链接到本地20000端口,超时时间3秒,超过则抛出超时异常
socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 3000);

System.out.println("已发起服务器连接,并进入后续流程~");
System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort());

try {
// 发送接收数据
todo(socket);
} catch (Exception e) {
System.out.println("异常关闭");
}

// 释放资源
socket.close();
System.out.println("客户端已退出~");

}

private static Socket createSocket() throws IOException {
/*
// 无代理模式,等效于空构造函数
Socket socket = new Socket(Proxy.NO_PROXY);

// 新建一份具有HTTP代理的套接字,传输数据将通过www.baidu.com:8080端口转发
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(Inet4Address.getByName("www.baidu.com"), 8800));
socket = new Socket(proxy);

// 新建一个套接字,并且直接链接到本地20000的服务器上
socket = new Socket("localhost", PORT);

// 新建一个套接字,并且直接链接到本地20000的服务器上
socket = new Socket(Inet4Address.getLocalHost(), PORT);

// 新建一个套接字,并且直接链接到本地20000的服务器上,并且绑定到本地20001端口上
socket = new Socket("localhost", PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
socket = new Socket(Inet4Address.getLocalHost(), PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
*/

Socket socket = new Socket();
// 绑定到本地20001端口
socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), LOCAL_PORT));

return socket;
}

private static void initSocket(Socket socket) throws SocketException {
// 设置读取超时时间为2秒
socket.setSoTimeout(2000);

// 是否复用未完全关闭的Socket地址,对于指定bind操作后的套接字有效
socket.setReuseAddress(true);

// 是否开启Nagle算法,收到所有发送包后再回送收到,而不是收一次回复一次
socket.setTcpNoDelay(true);

// 是否需要在长时无数据响应时发送确认数据(类似心跳包),时间大约为2小时
socket.setKeepAlive(true);

// 对于close关闭操作行为进行怎样的处理;默认为false,0
// false、0:默认情况,关闭时立即返回,底层系统接管输出流,将缓冲区内的数据发送完成
// true、0:关闭时立即返回,缓冲区数据抛弃,直接发送RST结束命令到对方,并无需经过2MSL等待
// true、200:关闭时最长阻塞200毫秒,随后按第二情况处理
socket.setSoLinger(true, 20);

// 是否让紧急数据内敛,默认false;紧急数据通过 socket.sendUrgentData(1);发送
socket.setOOBInline(true);

// 设置接收发送缓冲器大小
socket.setReceiveBufferSize(64 * 1024 * 1024);
socket.setSendBufferSize(64 * 1024 * 1024);

// 设置性能参数:短链接,延迟,带宽的相对重要性(权重)
socket.setPerformancePreferences(1, 1, 0);
}

private static void todo(Socket client) throws IOException {
// 得到Socket输出流
OutputStream outputStream = client.getOutputStream();

// 得到Socket输入流
InputStream inputStream = client.getInputStream();
byte[] buffer = new byte[256];
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);

// byte
byteBuffer.put((byte) 126);

// char
char c = 'a';
byteBuffer.putChar(c);

// int
int i = 2323123;
byteBuffer.putInt(i);

// bool
boolean b = true;
byteBuffer.put(b ? (byte) 1 : (byte) 0);

// Long
long l = 298789739;
byteBuffer.putLong(l);


// float
float f = 12.345f;
byteBuffer.putFloat(f);


// double
double d = 13.31241248782973;
byteBuffer.putDouble(d);

// String
String str = "Hello你好!";
byteBuffer.put(str.getBytes());

// 发送到服务器
outputStream.write(buffer, 0, byteBuffer.position() + 1);

// 接收服务器返回
int read = inputStream.read(buffer);
System.out.println("收到数量:" + read);

// 资源释放
outputStream.close();
inputStream.close();
}
}

Server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public class Server {
private static final int PORT = 20000;

public static void main(String[] args) throws IOException {
//创建服务端
ServerSocket server = createServerSocket();
//初始化
initServerSocket(server);

// 绑定到本地端口上
server.bind(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 50);


System.out.println("服务器准备就绪~");
System.out.println("服务器信息:" + server.getInetAddress() + " P:" + server.getLocalPort());


// 等待客户端连接
for (; ; ) {
// 得到客户端
Socket client = server.accept();
// 客户端构建异步线程
ClientHandler clientHandler = new ClientHandler(client);
// 启动线程
clientHandler.start();
}

}

private static ServerSocket createServerSocket() throws IOException {
// 创建基础的ServerSocket
ServerSocket serverSocket = new ServerSocket();

// 绑定到本地端口20000上,并且设置当前可允许等待链接的队列为50个
//serverSocket = new ServerSocket(PORT);

// 等效于上面的方案,队列设置为50个
//serverSocket = new ServerSocket(PORT, 50);

// 与上面等同
// serverSocket = new ServerSocket(PORT, 50, Inet4Address.getLocalHost());

return serverSocket;
}

private static void initServerSocket(ServerSocket serverSocket) throws IOException {
// 是否复用未完全关闭的地址端口
serverSocket.setReuseAddress(true);

// 等效Socket#setReceiveBufferSize
serverSocket.setReceiveBufferSize(64 * 1024 * 1024);

// 设置serverSocket#accept超时时间
// serverSocket.setSoTimeout(2000);

// 设置性能参数:短链接,延迟,带宽的相对重要性
serverSocket.setPerformancePreferences(1, 1, 1);
}

/**
* 客户端消息处理
*/
private static class ClientHandler extends Thread {
private Socket socket;

ClientHandler(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
super.run();
System.out.println("新客户端连接:" + socket.getInetAddress() +
" P:" + socket.getPort());

try {
// 得到套接字流
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();

byte[] buffer = new byte[256];
int readCount = inputStream.read(buffer);
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, readCount);

// byte
byte be = byteBuffer.get();

// char
char c = byteBuffer.getChar();

// int
int i = byteBuffer.getInt();

// bool
boolean b = byteBuffer.get() == 1;

// Long
long l = byteBuffer.getLong();

// float
float f = byteBuffer.getFloat();

// double
double d = byteBuffer.getDouble();

// String
int pos = byteBuffer.position();
String str = new String(buffer, pos, readCount - pos - 1);

System.out.println("收到数量:" + readCount + " 数据:"
+ be + "\n"
+ c + "\n"
+ i + "\n"
+ b + "\n"
+ l + "\n"
+ f + "\n"
+ d + "\n"
+ str + "\n");

outputStream.write(buffer, 0, readCount);
outputStream.close();
inputStream.close();

} catch (Exception e) {
System.out.println("连接异常断开");
} finally {
// 连接关闭
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}

System.out.println("客户端已退出:" + socket.getInetAddress() +
" P:" + socket.getPort());

}
}
}