长连接及Java Socket实现
Socket默认是不关闭的,除非手动关闭,建立连接的两端可互相发送信息,连接的长短主要针对的是连接的时间,长时间不关闭的连接即长连接,短连接即建立连接的两端在发送一次或几次数据后很快关闭Socket的连接。
以下是Tcp、UDP的字节流和字符流的Socket使用,可用其直接传输文件及字符数据。
Tcp.服务端
复制代码
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164package com.td.socket; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Tcp * 短连接:建立连接的两端在发送一次或几次数据后很快关闭Socket的连接 * 长连接:Socket默认是不关闭的,除非手动关闭,长时间不关闭的连接即长连接,建立连接的两端可互相发送信息 * 长连接实现:不主动关闭建立连接的两端,通过流的flush()发送数据 */ public class SocketTCPServer { private static ExecutorService threadPool = Executors.newFixedThreadPool(100); private static final int characterPort = 55533; private static final int bytePort = 55534; public static File writeToFile1 = new File("E:\logs\copied1.pdf"); public static File writeToFile2 = new File("E:\logs\copied2.pdf"); public static void main(String[] args) { try { startSocketServerCharacter(); startSocketServerByte(); } catch (Exception e) { System.out.println(e.toString()); } } /** * 字符流-启动socket */ public static void startSocketServerCharacter() throws Exception { ServerSocket serverSocket = new ServerSocket(characterPort); while (true) { //等待客户端的连接 Socket socket = serverSocket.accept(); // socket.setReuseAddress(true); //使用 bind(SocketAddress)时,关闭 TCP 连接时,该连接可能在关闭后的一段时间内保持超时状态,不建议使用bind(),不建议使用次配置 // socket.setSoLinger(true, 65535); //启用/禁用具有指定逗留时间的 SO_LINGER,Socket会等待指定的时间发送完数据包,当数据量发送过大抛出异常时,再来设置这个值 // socket.setTcpNoDelay(true); //客户向服务器发送数据的时候,会根据当前数据量来决定是否发送,如果数据量过小,那么系统将会根据Nagle 算法(暂时还没研究),来决定发送包的合并,也就是说发送会有延迟,默认开启。这个操作可能导致拆包和粘包 // socket.setSendBufferSize(10); //限制发送的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 // socket.setReceiveBufferSize(10); //限制接收的的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 socket.setKeepAlive(true); //构建长连接的Socket还是配置上SO_KEEPALIVE比较好,默认2小时无数据交互时发送探测包,周期为75秒,失败重连次数为9次,不同系统配置不一样 threadPool.submit(new Runnable() { @Override public void run() { try ( InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ) { characterMsgReader(inputStream, outputStream); socket.shutdownOutput(); // 长连接则不关闭输出流 socket.shutdownInput(); // 长连接则不关闭输入流 } catch (Exception e) { System.out.println(e.toString()); } } }); } } /** * 字符流-解析客户端的消息 */ public static void characterMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String str; //收到断行符"n"则读出一行 while((str=bufferedReader.readLine()) != null) { // 如果不关闭接收流的话,bufferedReader.readLine()会一直阻塞,等待客户端传递消息 System.out.println("客户端消息:收到一条来自客户端的消息: " + str); characterMsgWriter(outputStream, inputStream, str); } System.out.println("end"); } /** * 字符流-发送给客户端回执消息 */ public static void characterMsgWriter(OutputStream outputStream, InputStream inputStream, String str) throws Exception { BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); str = "消息已收到"; bufferedWriter.write(str + " - 第一次回执"); bufferedWriter.write("n"); bufferedWriter.flush(); // 第一次回执消息,长连接实现方式 } /** * 字节流-启动socket */ public static void startSocketServerByte() throws Exception { ServerSocket serverSocket = new ServerSocket(bytePort); while (true) { //等待客户端的连接 Socket socket = serverSocket.accept(); // socket.setReuseAddress(true); //使用 bind(SocketAddress)时,关闭 TCP 连接时,该连接可能在关闭后的一段时间内保持超时状态,不建议使用bind(),不建议使用次配置 // socket.setSoLinger(true, 65535); //启用/禁用具有指定逗留时间的 SO_LINGER,Socket会等待指定的时间发送完数据包,当数据量发送过大抛出异常时,再来设置这个值 // socket.setTcpNoDelay(true); //客户向服务器发送数据的时候,会根据当前数据量来决定是否发送,如果数据量过小,那么系统将会根据Nagle 算法(暂时还没研究),来决定发送包的合并,也就是说发送会有延迟,默认开启。这个操作可能导致拆包和粘包 // socket.setSendBufferSize(10); //限制发送的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 // socket.setReceiveBufferSize(10); //限制接收的的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 socket.setKeepAlive(true); //构建长连接的Socket还是配置上SO_KEEPALIVE比较好,默认2小时无数据交互时发送探测包,周期为75秒,失败重连次数为9次,不同系统配置不一样 threadPool.submit(new Runnable() { @Override public void run() { try ( InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ) { byteMsgReader(inputStream, outputStream); socket.shutdownOutput(); // 长连接则不关闭输出流 socket.shutdownInput(); // 长连接则不关闭输入流 } catch (Exception e) { System.out.println(e.toString()); } } }); } } /** * 字节流-解析客户端的消息 */ public static void byteMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception { BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); FileOutputStream bufferedOutputStream1 = new FileOutputStream(writeToFile1, false); FileOutputStream bufferedOutputStream2 = new FileOutputStream(writeToFile2, false); byte[] b = new byte[4096]; int i = 0; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream1.write(b, 0, i); bufferedOutputStream1.flush(); if (i < b.length) { // 根据读取的长度是否满格判断当前文件是否已读取完毕 bufferedOutputStream1.close(); break; } } System.out.println("接收完成第一个文件"); byteMsgWriter(outputStream); i = 0; byte[] b2 = new byte[4096]; while ((i = bufferedInputStream.read(b2)) > 0) { bufferedOutputStream2.write(b2, 0, i); bufferedOutputStream2.flush(); if (i < b2.length) { // 根据读取的长度是否满格判断当前文件是否已读取完毕 bufferedOutputStream2.close(); break; } } System.out.println("接收完成第二个文件"); byteMsgWriter(outputStream); } /** * 字节流-发送给客户端回执消息 */ public static void byteMsgWriter(OutputStream outputStream) throws Exception { BufferedOutputStream bufferedOutputStreamBack = new BufferedOutputStream(outputStream); bufferedOutputStreamBack.write("服务端已成功接收文件.".getBytes("UTF-8")); bufferedOutputStreamBack.write("n".getBytes("UTF-8")); bufferedOutputStreamBack.flush(); // 第一次发送消息,长连接实现方式 } }
Tcp.客户端
复制代码
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
130package com.td.socket; import java.io.*; import java.net.Socket; public class SocketTCPClient { private static final String host = "127.0.0.1"; private static final int characterPort = 55533; private static final int bytePort = 55534; public static File readFile = new File("E:\logs\file.pdf"); public static void main(String args[]) throws Exception { transportToServer(); } /** * 传输数据到服务端 */ public static void transportToServer() { try { startSocketClientByte(); startSocketClientCharacter(); } catch (Exception e) { e.printStackTrace(); } } /** * 字节流-启动socket */ public static void startSocketClientByte() throws Exception { Socket socket = new Socket(host, bytePort); // socket.setReuseAddress(true); //使用 bind(SocketAddress)时,关闭 TCP 连接时,该连接可能在关闭后的一段时间内保持超时状态,不建议使用bind(),不建议使用次配置 // socket.setSoLinger(true, 65535); //启用/禁用具有指定逗留时间的 SO_LINGER,Socket会等待指定的时间发送完数据包,当数据量发送过大抛出异常时,再来设置这个值 // socket.setTcpNoDelay(true); //客户向服务器发送数据的时候,会根据当前数据量来决定是否发送,如果数据量过小,那么系统将会根据Nagle 算法(暂时还没研究),来决定发送包的合并,也就是说发送会有延迟,默认开启。这个操作可能导致拆包和粘包 // socket.setSendBufferSize(10); //限制发送的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 // socket.setReceiveBufferSize(10); //限制接收的的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 socket.setKeepAlive(true); //构建长连接的Socket还是配置上SO_KEEPALIVE比较好,默认2小时无数据交互时发送探测包,周期为75秒,失败重连次数为9次,不同系统配置不一样 InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); byteMsgWriter(outputStream, inputStream); socket.shutdownOutput(); // 长连接则不关闭输出流 socket.shutdownInput(); // 长连接则不关闭输入流 } /** * 字节流-发送给服务端 */ public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(readFile)); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); //mark后读取超过readlimit字节数据,mark标记就会失效 bufferedInputStream.mark(909600000); byte[] b = new byte[4096]; int i = 0; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第一次发送消息,长连接实现方式 } bufferedInputStream.reset(); Thread.sleep(1); // 必须加休眠,否则第二个文件流会发生错乱 characterMsgReader(bufferedReader); // 从服务端接收消息 i = 0; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第二次发送消息,长连接实现方式 } characterMsgReader(bufferedReader); // 从服务端接收消息 } /** * 字符流-启动socket */ public static void startSocketClientCharacter() throws Exception { Socket socket = new Socket(host, characterPort); // socket.setReuseAddress(true); //使用 bind(SocketAddress)时,关闭 TCP 连接时,该连接可能在关闭后的一段时间内保持超时状态,不建议使用bind(),不建议使用次配置 // socket.setSoLinger(true, 65535); //启用/禁用具有指定逗留时间的 SO_LINGER,Socket会等待指定的时间发送完数据包,当数据量发送过大抛出异常时,再来设置这个值 // socket.setTcpNoDelay(true); //客户向服务器发送数据的时候,会根据当前数据量来决定是否发送,如果数据量过小,那么系统将会根据Nagle 算法(暂时还没研究),来决定发送包的合并,也就是说发送会有延迟,默认开启。这个操作可能导致拆包和粘包 // socket.setSendBufferSize(10); //限制发送的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 // socket.setReceiveBufferSize(10); //限制接收的的流大小,默认都是8K,如果有需要可以修改,通过相应的set方法。不建议修改的太小,设置太小数据传输将过于频繁。太大了将会造成消息停留。setTcpNoDelay为true时设置无效 socket.setKeepAlive(true); //构建长连接的Socket还是配置上SO_KEEPALIVE比较好,默认2小时无数据交互时发送探测包,周期为75秒,失败重连次数为9次,不同系统配置不一样 String data = "来自客户端的字符流消息"; InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); characterMsgWriter(outputStream, data, inputStream); // socket.shutdownOutput(); //通过心跳关闭socket输出流 // socket.shutdownInput(); //通过心跳关闭socket输入流 } /** * 字符流-发送给服务端 */ public static void characterMsgWriter(OutputStream outputStream, String data, InputStream inputStream) throws Exception { BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); bufferedWriter.write(data); bufferedWriter.write("n"); bufferedWriter.flush(); // 第一次发送消息,长连接实现方式 characterMsgReader(bufferedReader); // 从服务端接收消息 bufferedWriter.write(data); bufferedWriter.write("n"); bufferedWriter.flush(); // 第二次发送消息,长连接实现方式 characterMsgReader(bufferedReader); // 从服务端接收消息 } /** * 字符流-解析服务端的回执消息-单次解析 */ public static void characterMsgReader(BufferedReader bufferedReader) throws Exception { System.out.println("服务端消息: " + bufferedReader.readLine()); } /** * 字符流-解析服务端的回执消息-不间断解析 * */ public static void characterMsgReader(InputStream inputStream) throws Exception { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println("服务端消息: " + line); } } }
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
47
48
49
50
51
52
53package com.td.socket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * UDP同理 * 短连接:建立连接的两端在发送一次或几次数据后很快关闭Socket的连接 * 长连接:Socket默认是不关闭的,除非手动关闭,长时间不关闭的连接即长连接,建立连接的两端可互相发送信息 * 长连接实现:不主动关闭建立连接的两端,通过流的flush()发送数据 */ public class SocketUDPServer { public static void main(String[] args) { try { int port = 10010; DatagramSocket socket = new DatagramSocket(port); DatagramPacket reply = receiveUDP(socket); sendUDP(reply.getAddress(), reply.getPort(), "welcome to server!", socket); sendUDP(reply.getAddress(), reply.getPort(), "welcome to server!", socket); // socket.close(); } catch (IOException e) { e.printStackTrace(); } } /** * UDP接收报文后发送报文 * @param port 服务端收文后通过DatagramPacket获取"动态"Port及来访IP */ public static void sendUDP(InetAddress inetAddress, int port, Object dataObj, DatagramSocket socket) throws IOException { byte[] data = dataObj.toString().getBytes(); DatagramPacket packet = new DatagramPacket(data, data.length, inetAddress, port); socket.send(packet); } /** * UDP接收报文 */ public static DatagramPacket receiveUDP(DatagramSocket socket) throws IOException { byte[] dataReply = new byte[1024]; DatagramPacket packet = new DatagramPacket(dataReply, dataReply.length); socket.receive(packet); String reply = new String(dataReply, 0, packet.getLength()); System.out.println("第一次收到客户端消息 : " + reply); String reply2 = new String(dataReply, 0, packet.getLength()); System.out.println("第二次收到客户端消息 : " + reply2); return packet; } }
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
47
48
49
50
51
52package com.td.socket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; public class SocketUDPClient { public static void main(String[] args) { try { DatagramSocket socket = new DatagramSocket(); sendUDP("127.0.0.1", 10010, "send data to server.", socket); System.out.println("receive from server:" + (String) receiveUDP(socket)); // socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 客户端发送报文 */ public static void sendUDP(String host, int port, Object dataObj, DatagramSocket socket) throws IOException { InetAddress address = InetAddress.getByName(host); byte[] data = dataObj.toString().getBytes(); DatagramPacket packet = new DatagramPacket(data, data.length, address, port); socket.send(packet); } /** * 客户端发送后接收报文,通过DatagramSocket为中介建立起新的接收Port */ public static Object receiveUDP(DatagramSocket socket) throws IOException { byte[] dataReply = new byte[1024]; DatagramPacket packet2 = new DatagramPacket(dataReply, dataReply.length); socket.receive(packet2); String reply = new String(dataReply, 0, packet2.getLength()); System.out.println("第一次收到数据" + reply); socket.receive(packet2); String reply2 = new String(dataReply, 0, packet2.getLength()); System.out.println("第二次收到数据" + reply2); return reply; } }
最后
以上就是还单身乌龟最近收集整理的关于长连接及Java Socket实现长连接及Java Socket实现的全部内容,更多相关长连接及Java内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复