欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > 手写模拟基于Socket通信协议

手写模拟基于Socket通信协议

2025/1/11 7:17:09 来源:https://blog.csdn.net/qq_43750656/article/details/145043808  浏览:    关键词:手写模拟基于Socket通信协议
  • 服务端
package com.ssm3.ssm3.Socket.http;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.SocketHandler;public class TomcatServer {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8080);while (true) {Socket socket = serverSocket.accept();new Thread(() -> {try {InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream();while (true) {//创建一个字节集合List<Byte> contentBytes = new ArrayList<Byte>();//先读取第一行while (true) {int byteData = is.read();contentBytes.add((byte) byteData);
//                            System.out.println("byteToStringStr" + byteToStringStr(contentBytes));if (byteData == 10 && (countBytes(contentBytes, (byte) 10) == 1)) {//读取到了换行符System.out.println("Http请求行:" + byteToString(contentBytes));}//继续判断是否是以 \r\n\r\n 结束,表示请求头结束了if (endWithHeader(contentBytes)){System.out.println("完成的Http协议请求: \n" + byteToString(contentBytes));break;}}os.write(("已经到请求,共计字节数:" + contentBytes.size() + "\r\n").getBytes(StandardCharsets.UTF_8) );os.flush();}} catch (IOException e) {e.printStackTrace();}}).start();}}public static Integer countBytes(List<Byte> byteList,byte target) {Map<Byte, Integer> countMap = new HashMap<>();for (Byte b : byteList) {if (countMap.containsKey(b)) {countMap.put(b, countMap.get(b) + 1);} else {countMap.put(b, 1);}}return countMap.get((byte)target) == null ? 0 : countMap.get(target);}private static boolean endWithHeader(List<Byte> contentBytes) {if (contentBytes.size() < 4) {return false;}Byte last1 = contentBytes.get(contentBytes.size() - 1);Byte last2 = contentBytes.get(contentBytes.size() - 2);Byte last3 = contentBytes.get(contentBytes.size() - 3);Byte last4 = contentBytes.get(contentBytes.size() - 4);if (last1 == '\n' && last2 == '\r' && last3 == '\n' && last4 == '\r') {return true;}return false;}private static String byteToStringStr(List<Byte> contentBytes) {return Arrays.toString(contentBytes.toArray());}private static String byteToString(List<Byte> contentBytes) {byte[] byteArray = new byte[contentBytes.size()];for (int i = 0; i < contentBytes.size(); i++) {byteArray[i] = contentBytes.get(i);}String str = new String(byteArray);return str;}}
  • 客户端
package com.ssm3.ssm3.Socket.http;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {Socket socket = new Socket("127.0.0.1",8080);InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));OutputStream os = socket.getOutputStream();Scanner sc = new Scanner(System.in);while(true){System.out.print("请输入Http请求行,回车键结束 : ");String httpLine = sc.nextLine();httpLine = httpLine + "\r\n";os.write(httpLine.getBytes());os.flush();System.out.print("请输入Http请求头,回车键结束 : ");String httpHeader = sc.nextLine();httpHeader = httpHeader + "\r\n\r\n";os.write(httpHeader.getBytes());os.flush();String serverResponse = br.readLine();System.out.println("服务器响应:" + serverResponse);}}}
  • ServerSocket与Socket

ServerSocket 是启动一个服务端监听程序的入口,当绑定一个端口号时,操作系统会进行端口号与应用程序进程ID绑定,从而知道这个端口号关联的进程ID,将来停止进程时,对应的端口也会进行清理。

  • 服务端ServerSocket请求队列

一个ServerSocket背后关联了一个请求队列,当客户端发送连接请求时,操作系统会将这个连接请求存入到当前ServerSocket的请求队列中,当ServerSocket调用accept方法,表示允许服务器接收一个客户端的连接请求,此时就会从队列中取出一个连接,操作系统建立好连接以后,创建一个新的Socket对象并返回 ,这个Socket对象绑定了进程Id,服务器IP和端口,客户端IP和端口。

  • 网络输入输出流
    一个Socket对象代表了一个与唯一一台物理设备上的唯一一个端口号进行的通信,通过Socket可以获取这个通信关联的输入流InputStream和输出流OutputStream双向通道,当通过输出流发送数据包时,字节数据会被网络协议栈最终转换为数字信号,然后通过介质进行传送。

  • 输出输出的边界是?

网络通信中,输入流和文件的输入流略有差异,虽然两者都是从外部设备(磁盘、网卡) 读取数据到内存,但是对于对于文件的输入流来说,文件大小是固定的,一个流可以读取到的内容是有限的,而对于一个网络输入流来说,发送方可以无限的传输数据,那服务器或者客户端如何知道想要的数据 已经传输完成了呢? 这就需要两个共同遵循一种协议,例如Http协议、FTP协议,协议双方会约定好每次传送的内容格式以及大小,这样双方在接收到对方的数据时,就可以按照协议约定的去解析并处理。例如,对于缓冲流的readline和writeline方法其实也可以看作是一种协议,那就是双方约定好都按以读取到 \r\n换行符结束。

  • 缓冲流write方法不发送数据
    对于一些包装后的缓冲流,仅调用write方法可能不会将数据发送到网络中,因为还没有到达缓冲区的阈值,此时需要强制调用flush方法将缓冲区的数据一次性刷新出去。

  • 网络协议栈传输数据示例图

在这里插入图片描述

  • 请求连接建立过程

网络协议栈解析数据包 -> 最后的传输层解析出用户数据 -> 操作系统根据发送与目标端口号找到对应的ServerSocket,将连接请求存入队列 -> 应用程序的accecpt方法从队列中获取连接请求 -> 建立双向socket连接。

  • 数据包传输
    网络协议栈解析数据包 -> 最后的传输层解析出用户数据 -> 操作系统根据发送的端口号与目标端口号找到对应的socket,将字节数据缓冲到该socket的inputstream的字节队列中,当inputstream调用read方法时,就可以读取到对应的字节数据,当然read方法是阻塞的,BIO的。-> 读取到数据后进行应用程序处理 -> 处理完响应 -> 继续等待下一次处理。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com