当前位置:   article > 正文

TCP协议简单介绍_通信协议分割符和转义

通信协议分割符和转义
  1. TCP是TCP协议中非常重要和常用的通信协议,可以实现可靠的网络通信

     特点:
     需要创建连接 需要三次握手
     底层建立的连接流 数据包以流的方式传递 没有传输数据量大小的限制
     传输过程中 可以保证数据一定不会丢 也不会多 也可以保证 顺序的一致
     
     速度比较慢在可靠性要求比较高 速度要求比较低 的场景下优先使用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

2. Java中实现TCP
在TCP通信中,通信的过程需要两端的参与,其中发起请求的端称之为客户端 被动等待请求的端称之为服务器端

在这里插入图片描述
案例:实现TPC通信
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

		/**
		 * TCP通信的客户端
		 */
		public class Demo01Client {
			public static void main(String[] args) throws Exception {
				//1.创建Socket
				Socket socket = new Socket();
				//2.连接服务器
				socket.connect(new InetSocketAddress("127.0.0.1", 44444));
				//3.从客户端发送消息给服务端
				OutputStream out = socket.getOutputStream();
				//4.向服务端发送数据
				String msg = "hello world~ ";
				out.write(msg.getBytes());
				out.flush();
				//5.从服务端接受数据
				InputStream in = socket.getInputStream();
				byte [] data = new byte[1024];
				int len = in.read(data);
				String msg2 = new String(data,0,len);
				System.out.println(msg2);
				//6.关闭套接字
				socket.close();
			}
		}
		
		
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.InetSocketAddress;
		import java.net.ServerSocket;
		import java.net.Socket;
		
		/**
		 * TCP通信的服务端
		 */
		public class Demo01Server {
			public static void main(String[] args) throws Exception {
				//1.创建服务端
				ServerSocket ss = new ServerSocket();
				//2.绑定指定端口
				ss.bind(new InetSocketAddress(44444));
				//3.等待客户端连接
				Socket socket = ss.accept();
				//4.接受客户端的数据
				InputStream in = socket.getInputStream();
				byte [] data = new byte[1024];
				int len = in.read(data);
				String str = new String(data,0,len);
				System.out.println(str);
				//5.向客户端返回数据
				String msg = "hello net~";
				OutputStream out = socket.getOutputStream();
				out.write(msg.getBytes());
				out.flush();
				//6.关闭套接字
				socket.close();
				ss.close();
			}
		}
  • 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

3. 在tcp网络通信中应用多线程技术实现一个服务端为多个客户端提供服务
在这里插入图片描述
案例:实现文件上传服务器 并且 通过多线程技术来实现同时处理多个客户端的效果
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

		/**
		 * TCP案例:通过TCP实现文件上传 - 服务端代码
		 */
		
		public class Demo02UploadServer {
			public static void main(String[] args) throws Exception {
				//1.创建服务端
				ServerSocket ss = new ServerSocket();
				ss.bind(new InetSocketAddress(44444));
				System.out.println("###千度网盘开始运行了###");
				//2.不停接受客户端连接,一旦连接成功,交给线程处理
				while(true){
					Socket socket = ss.accept();
					new Thread(new UploadRunnable(socket)).start();
				}
			}
		}
		
		class UploadRunnable implements Runnable{
			private Socket socket = null;
			public UploadRunnable(Socket socket) {
				this.socket  = socket;
			}
			
			@Override
			public void run() {
				OutputStream out = null;
				try {
					//1.获取socket输入流
					InputStream in = socket.getInputStream();
					//2.创建文件输出流指向输出位置
					String path = "upload/"+UUID.randomUUID().toString()+".data";
					out = new FileOutputStream(path);
					//3.对接流
					byte [] data = new byte[1024];
					int i = 0;
					while((i = in.read(data))!=-1){
						out.write(data,0,i);
					}
					System.out.println("接收到了来自["+socket.getInetAddress().getHostAddress()+"]的上传文件["+path+"]");
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					//4.关闭资源
					if(out!=null){
						try {
							out.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							out = null;
						}
					}
					if(socket != null){
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							socket = null;
						}
					}
				}
			}
		}
		

		
		import java.io.File;
		import java.io.FileInputStream;
		import java.io.FileNotFoundException;
		import java.io.IOException;
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.InetSocketAddress;
		import java.net.Socket;
		import java.util.Scanner;
		
		/**
		 * TCP案例:通过TCP实现文件上传 - 客户端代码
		 */
		public class Demo02UploadClient {
			public static void main(String[] args) {
				Scanner scanner = null;
				InputStream in = null;
				Socket socket = null;
				try {
					//1.要求用户输入文件路径
					scanner = new Scanner(System.in);
					System.out.println("--请输入要上传的文件的路径:");
					String path = scanner.nextLine();
					File file = new File(path);
					//2.只有文件存在 且 是一个文件才上传
					if(file.exists() && file.isFile()){
						//2.创建连接文件的输入流
						in = new FileInputStream(file);
						//3.创建TCP客户端对象
						socket = new Socket();
						//4.连接TCP服务端
						socket.connect(new InetSocketAddress("127.0.0.1",44444));
						//5.获取到TCP服务端的输出流
						OutputStream out = socket.getOutputStream();
						//6.对接流 发送文件数据给服务端
						byte [] data = new byte[1024];
						int i = 0;
						while((i = in.read(data))!=-1){
							out.write(data,0,i);
						}
					}else{
						throw new RuntimeException("文件不存在 或者是一个文件夹~~");
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally{
					//7.关闭扫描器 关闭文件输入流 关闭套接字
					if(scanner != null){
						scanner.close();
					}
					if(in != null){
						try {
							in.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							in = null;
						}
					}
					if(socket!=null){
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							socket = null;
						}
					}
				}
			}
		}
  • 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

4. TCP通信中的粘包问题
a. 粘包问题概述
TCP通信中 如果连续传输多段数据 ,TCP在传输的过程中,会根据需要自动的拆分包合并包,造成数据边界信息丢失了,在接收端收到数据后无法判断数据的边界在哪里,这样的问题就称之为TCP通信中的粘包问题。
粘包问题的本质在于TCP协议是传输层的协议,而数据边界判断的问题本质上是会话层的问题,TCP协议并没有给予解决方案。
而socket编程是给予网络层 和 传输层的编程,没有会话层的功能提供,所以在socket编程中粘包问题需要程序开发人员自己想办法解决
在这里插入图片描述
b. 粘包问题解决方案
i. 只传输固定长度的数据
能解决粘包问题,但是程序的灵活性非常低,只能在每次传输的数据长度都一致的情况下使用,应用的场景比较少
ii. 约定分隔符
能解决粘包问题,但是如果数据本身包含分隔符,则需要进行转义。转义的过程比较麻烦,浪费时间,代码写起来也比较复杂
iii. 使用协议
在通信双发原定数据传输的格式 发送方严格按照格式发送 接收方严格按照格式接收 从而根据格式本身判断数据的边界

			协议又分为公有协议 和 私有协议
			使用公有协议:
				利用会话层 传输层 应用层 的公有协议的规则 来传输数据 判断边界
				在全世界范围内通用 但协议相对复杂
			使用私有协议:
				自己来约定传输双方使用的格式 从而来判断边界
				协议可以根据需要定制 但只在有限的小范围内有效
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

案例:改造文件上传案例 通过自定义协议解决粘包问题 实现传输文件同时传输文件名
在这里插入图片描述
协议格式: [文件名长度]\r\n[文件名][文件内容长度]\r\n[文件内容]

		import java.io.File;
		import java.io.FileInputStream;
		import java.io.IOException;
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.InetSocketAddress;
		import java.net.Socket;
		import java.util.Scanner;
		
		/**
		 * TCP案例:通过TCP实现文件上传 - 客户端代码
		 */
		public class Demo02UploadClient {
			public static void main(String[] args) {
				Scanner scanner = null;
				InputStream in = null;
				Socket socket = null;
				try {
					//1.要求用户输入文件路径
					scanner = new Scanner(System.in);
					System.out.println("--请输入要上传的文件的路径:");
					String path = scanner.nextLine();
					File file = new File(path);
					//2.只有文件存在 且 是一个文件才上传
					if(file.exists() && file.isFile()){
						//2.创建连接文件的输入流
						in = new FileInputStream(file);
						//3.创建TCP客户端对象
						socket = new Socket();
						//4.连接TCP服务端
						socket.connect(new InetSocketAddress("127.0.0.1",44444));
						//5.获取到TCP服务端的输出流
						OutputStream out = socket.getOutputStream();
						//6.1向服务器发送[文件名字节长度\r\n]
						out.write((file.getName().getBytes().length+"\r\n").getBytes());
						//6.2向服务器发送[文件名字节]
						out.write(file.getName().getBytes());
						//6.3向服务器发送[文件内容字节长度\r\n]
						out.write((file.length()+"\r\n").getBytes());
						//6.4向服务器发送[文件内容字节]
						byte [] data = new byte[1024];
						int i = 0;
						while((i = in.read(data))!=-1){
							out.write(data,0,i);
						}
					}else{
						throw new RuntimeException("文件不存在 或者是一个文件夹~~");
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally{
					//7.关闭扫描器 关闭文件输入流 关闭套接字
					if(scanner != null){
						scanner.close();
					}
					if(in != null){
						try {
							in.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							in = null;
						}
					}
					if(socket!=null){
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							socket = null;
						}
					}
				}
			}
		}

		
		import java.io.FileOutputStream;
		import java.io.IOException;
		import java.io.InputStream;
		import java.io.OutputStream;
		import java.net.InetSocketAddress;
		import java.net.ServerSocket;
		import java.net.Socket;
		
		/**
		 * TCP案例:通过TCP实现文件上传 - 服务端代码
		 */
		
		public class Demo02UploadServer {
			public static void main(String[] args) throws Exception {
				//1.创建服务端
				ServerSocket ss = new ServerSocket();
				ss.bind(new InetSocketAddress(44444));
				System.out.println("###千度网盘开始运行了###");
				//2.不停接受客户端连接,一旦连接成功,交给线程处理
				while(true){
					Socket socket = ss.accept();
					new Thread(new UploadRunnable(socket)).start();
				}
			}
		}
		
		class UploadRunnable implements Runnable{
			private Socket socket = null;
			public UploadRunnable(Socket socket) {
				this.socket  = socket;
			}
			
			/**
			 * 通过私有协议传输数据 协议的格式为 [文件名长度\r\n文件名 文件长度\r\n文件内容]
			 */
			@Override
			public void run() {
				OutputStream out = null;
				try {
					//1.获取socket输入流
					InputStream in = socket.getInputStream();
					//2.获取文件名 - 读到第一个回车换行之前 截取出文件名的长度 接着读取这个长度的字节 就是文件名
					//--读取数据 直到遇到第一个回车换行
					//----每次从流中读取一个字节 转成字符串 拼到line上 只要line还不是\r\n结尾 就重复这个过程
					String line = "";
					byte [] tmp = new byte[1];
					while(!line.endsWith("\r\n")){
						in.read(tmp);
						line += new String(tmp);
					}
					//----读取到了 文件名长度\r\n 截掉\r\n 转成int 就是文件名的长度
					int len = Integer.parseInt(line.substring(0, line.length()-2));
					//----从流中接着读 len个字节 就是文件名
					byte [] data = new byte[len];
					in.read(data);
					String fname = new String(data);
					
					//3.读取文件内容  - 读到下一个回车换行之前 截取出文件内容的长度 接着读取这个长度的字节 就是文件内容
					String line2 = "";
					byte [] tmp2 = new byte[1];
					while(!line2.endsWith("\r\n")){
						in.read(tmp2);
						line2 += new String(tmp2);
					}
					//----读取到了 文件长度\r\n 截掉\r\n 转成int 就是文件的长度
					int len2 = Integer.parseInt(line2.substring(0, line2.length()-2));
					//4.从流中读取 文件长度个字节 就是文件内容 输出到文件中
					byte data2 [] = new byte[len2];
					in.read(data2);
					
					//5.创建文件输出流指向输出位置,将数据写出到流中
					String path = "upload/"+fname;
					out = new FileOutputStream(path);
					out.write(data2);
					System.out.println("接收到了来自["+socket.getInetAddress().getHostAddress()+"]的上传文件["+path+"]");
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					//6.关闭资源
					if(out!=null){
						try {
							out.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							out = null;
						}
					}
					if(socket != null){
						try {
							socket.close();
						} catch (IOException e) {
							e.printStackTrace();
						} finally {
							socket = null;
						}
					}
				}
			}
		}
  • 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
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/988081
推荐阅读
相关标签
  

闽ICP备14008679号