精华内容
下载资源
问答
  • java tcp 滑动窗口流量控制协议实现源码,如何实现滑动窗口协议进行流量控制。
  • JAVA实现TCP通信

    万次阅读 多人点赞 2018-05-03 14:29:38
    TCPJava支持 协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构、交换方式、包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议、TCP协议和UDP协议。现在TCP/IP协议族中的...

    TCP的Java支持
    协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构、交换方式、包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议、TCP协议和UDP协议。现在TCP/IP协议族中的主要socket类型为流套接字(使用TCP协议)和数据报套接字(使用UDP协议)。
    TCP协议提供面向连接的服务,通过它建立的是可靠地连接。Java为TCP协议提供了两个类:Socket类和ServerSocket类。一个Socket实例代表了TCP连接的一个客户端,而一个ServerSocket实例代表了TCP连接的一个服务器端,一般在TCP Socket编程中,客户端有多个,而服务器端只有一个,客户端TCP向服务器端TCP发送连接请求,服务器端的ServerSocket实例则监听来自客户端的TCP连接请求,并为每个请求创建新的Socket实例,由于服务端在调用accept()等待客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,因此要为每个Socket连接开启一个线程。服务器端要同时处理ServerSocket实例和Socket实例,而客户端只需要使用Socket实例。另外,每个Socket实例会关联一个InputStream和OutputStream对象,我们通过将字节写入套接字的OutputStream来发送数据,并通过从InputStream来接收数据。
    TCP连接的建立步骤:
    客户端向服务器端发送连接请求后,就被动地等待服务器的响应。典型的TCP客户端要经过下面三步操作:
    1. 创建一个Socket实例:构造函数向指定的远程主机和端口建立一个TCP连接;
    2. 通过套接字的I/O流与服务端通信;
    3. 使用Socket类的close方法关闭连接。
    服务端的工作是建立一个通信终端,并被动地等待客户端的连接。典型的TCP服务端执行如下操作:
    1. 创建ServerSocket对象,绑定并监听端口
    2. 通过accept监听客户端的请求
    3. 建立连接后,通过输出输入流进行读写操作
    4. 关闭相关资源

    一个程序为服务端,建立TCP服务端套接字。
    java源程序如下:

    import java.io.DataInputStream;//导入DataInputStream类
    import java.io.DataOutputStream;//导入DataOutputStream
    import java.io.IOException;//导入IOException类
    import java.net.Socket;//导入Socket类
    import java.util.Scanner;//导入Scanner类
    
    /**
     * 注意用到的输入输出流DataInputStream和DataOutputStream,成对出现,最好用字节流
     */
    // 客户端类
    public class ChatClient {//创建公共类
        private String host = "localhost";// 默认连接到本机
        private int port = 8189;// 默认连接到端口8189
    
        public ChatClient() {
    
        }
    
        // 连接到指定的主机和端口
        public ChatClient(String host, int port) {//构造方法
            this.host = host;//将构造方法的参数host传递给类变量host
            this.port = port;//将构造方法的参数port传递给类变量port
        }
    
        public void chat() {//chat方法
            try {
                // 连接到服务器
                Socket socket = new Socket(host, port);//创建Socket类对象
    
                try {
    
                    DataInputStream in = new DataInputStream(socket
                            .getInputStream());// 读取服务器端传过来信息的DataInputStream
    
                    DataOutputStream out = new DataOutputStream(socket
                            .getOutputStream());// 向服务器端发送信息的DataOutputStream
    
    
                    Scanner scanner = new Scanner(System.in);// 装饰标准输入流,用于从控制台输入
    
                    while (true) {
                        String send = scanner.nextLine();//读取控制台输入的内容
                        System.out.println("客户端:" + send);//输出键盘输出内容提示 ,也就是客户端向服务器端发送的消息
                        // 把从控制台得到的信息传送给服务器
                        out.writeUTF("客户端:" + send);//将客户端的信息传递给服务器             
                        String accpet = in.readUTF();// 读取来自服务器的信息
                        System.out.println(accpet);//输出来自服务器的信息
                    }
    
                } finally {
                    socket.close();//关闭Socket监听
                }
            } catch (IOException e) {//捕获异常
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {//主程序方法
            new ChatClient().chat();//调用chat方法
        }
    }
    

    另外一个程序为客户端,建立TCP客户端套接字。
    java源程序如下:

    import java.io.DataInputStream; //导入 DataInputStream类
    import java.io.DataOutputStream;//导入DataOutputStream类 
    import java.io.IOException;//导入IOException类
    import java.net.ServerSocket;//导入ServerSocket类   
    import java.net.Socket; //导入Socket 类 
    import java.util.Scanner; //导入Scanner类
    
    /** 
     * 模拟qq聊天功能: 实现客户端与服务器(一对一)的聊天功能,客户端首先发起聊天,输入的内容在服务器端和客户端显示, 
     * 然后服务器端也可以输入信息,同样信息也在客户端和服务器端显示 
     */  
    
    // 服务器类  
    public class ChatServer {//ChatServer类  
        private int port = 8189;// 默认服务器端口  
    
        public ChatServer() {
    
        }  
    
        // 创建指定端口的服务器  
        public ChatServer(int port) {//构造方法  
            this.port = port;//将方法参数赋值给类参数  
        }  
    
        // 提供服务  
        public void service() {//创建service方法  
            try {// 建立服务器连接  
                ServerSocket server = new ServerSocket(port);//创建  ServerSocket类             
                Socket socket = server.accept();// 等待客户连接  
                try {                  
                    DataInputStream in = new DataInputStream(socket  
                            .getInputStream());// 读取客户端传过来信息的DataInputStream                   
                    DataOutputStream out = new DataOutputStream(socket  
                            .getOutputStream());// 向客户端发送信息的DataOutputStream                     
                    Scanner scanner = new Scanner(System.in);//从键盘接受数据  
                    while (true) {                       
                        String accpet = in.readUTF();// 读取来自客户端的信息   
                        System.out.println(accpet);//输出来自客户端的信息  
                        String send = scanner.nextLine();//nextLine方式接受字符串  
                        System.out.println("服务器:" + send);//输出提示信息                        
                        out.writeUTF("服务器:" + send);//把服务器端的输入发给客户端   
                    }  
                } finally {// 建立连接失败的话不会执行socket.close();
                    socket.close();//关闭连接 
                    server.close();//关闭                
                }  
            } catch (IOException e) {//捕获异常
                e.printStackTrace();  
            }  
        }  
    
        public static void main(String[] args) {//主程序方法  
            new ChatServer().service();//调用 service方法 
        }  
    }  
    

    注:在运行前要建立两个控制台窗口
    运行结果如下:
    这里写图片描述

    展开全文
  • Java实现ModbusTCP通信

    千次阅读 多人点赞 2019-09-05 11:25:49
    Java实现ModbusTCP通信

    不回复任何问题,有问题可以评论。

    一个项目,需要用Java实现使用ModbusTCP和硬件设备通信

    视频地址:https://www.bilibili.com/video/BV1cz4y1R7cg

    资料

    代码下载

    官网资料

    关于Java的开源库

    • Jamod:Java Modbus实现:Java Modbus库。该库由Dieter Wimberger实施。
    • ModbusPal:ModbusPal是一个正在进行的Java项目,用于创建逼真的Modbus从站模拟器。由于预定义的数学函数和/或Python脚本,寄存器值是动态生成的。ModbusPal依赖于RxTx进行串行通信,而Jython则依赖于脚本支持。
    • Modbus4J:Serotonin Software用Java编写的Modbus协议的高性能且易于使用的实现。支持ASCII,RTU,TCP和UDP传输作为从站或主站,自动请求分区,响应数据类型解析和节点扫描。
    • JLibModbus:JLibModbus是java语言中Modbus协议的一种实现。jSSC和RXTX用于通过串行端口进行通信。该库是一个经过积极测试和改进的项目。

    博客资料

    Github资料

    ModbusTCP协议

    Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。

    Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。

    标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用master/slave方式通信。

    Modbus协议是一个master/slave架构的协议。有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点。每一个slave设备都有一个唯一的地址。

    M和S.png

    个人感觉:

    • modbus协议也是对地址变量进行取或者入操作,变化的可能是地址变量的地址数据类型

    • 这个功能码(指定要做什么,指定存储器,然后指定动作:是读啊,是写啊,还是对多个一起操作啊)

    • Modbus和RS485的关系:Modbus是协议,物理层接口有RS232、RS422、RS485和以太网接口几种

    • 操作:新建四个不同功能码的窗口,然后运行代码,修改仿真软件上的值。

    代码参数的理解

    • saveid:看资料"从站在modbus总线上可以有多个",仿真软件就能模拟一个从站,就是ID=1,当然可以修改成ID=2
    • 功能码:4个功能码,对应写4个方法,,仿真软件上的F=1,或者F=2,3,4
    • addr:一开始看代码4个方法addr都是从0开始,是否重复?答案是:4个功能码表示4个区域或者设备,addr表示各自区域的地址编号。

    仿真软件

    验证4个常用功能码,仿真软件上面有F=01,F=02,F=03和F=04来显示

    • 0x01:读线圈
    • 0x02:读离散量输入
    • 0x03:读保持寄存器
    • 0x04:读输入寄存器

    对应的代码要写4个方法

    我要写一个Master(主站),所以需要一个Slave(从站)

    选择TCP模式,端口是固定的502

    地址类型

    F8:

    Slave Definition

    功能码

    数据类型

    功能码01


    功能码02


    功能码03,选择Float类型

    signed:有符号
    unsigned:无符号
    hex:十六进制
    binary:二进制

    big-endian:大端,将高序字节存储在起始地址(高位编址)
    little-endian:小端,将低序字节存储在起始地址(低位编址)

    swap:交换

    双击第一个地址输入数据,会提示输入数据的类型,32位数据占2个地址,所以下一个地址是--


    功能码04

    使用jlibmodbus

    特别有意思:常用的串口通信库都加进去了

    image.png

    image.png

    maven依赖

    <dependency>
    	<groupId>com.intelligt.modbus</groupId>
    	<artifactId>jlibmodbus</artifactId>
    	<version>1.2.9.7</version>
    </dependency>
    

    image.png

    测试功能码04

    在这里插入图片描述

    package com.tcb.jlibmodbus;
    
    import java.net.InetAddress;
    
    import com.intelligt.modbus.jlibmodbus.Modbus;
    import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
    import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
    import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
    import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
    import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
    import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
    
    
    /**
     * Hello world!
     *
     */
    public class App {
    	public static void main(String[] args) {
    		try {
    			// 设置主机TCP参数
    			TcpParameters tcpParameters = new TcpParameters();
     
    			// 设置TCP的ip地址
    			InetAddress adress = InetAddress.getByName("127.0.0.1");
     
    			// TCP参数设置ip地址
    			// tcpParameters.setHost(InetAddress.getLocalHost());
    			tcpParameters.setHost(adress);
     
    			// TCP设置长连接
    			tcpParameters.setKeepAlive(true);
    			// TCP设置端口,这里设置是默认端口502
    			tcpParameters.setPort(Modbus.TCP_PORT);
     
    			// 创建一个主机
    			ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
    			Modbus.setAutoIncrementTransactionId(true);
     
    			int slaveId = 1;//从机地址
    			int offset = 0;//寄存器读取开始地址
    			int quantity = 10;//读取的寄存器数量
     
     
    			try {
    				if (!master.isConnected()) {
    					master.connect();// 开启连接
    				}
     
    				// 读取对应从机的数据,readInputRegisters读取的写寄存器,功能码04
    				int[] registerValues = master.readInputRegisters(slaveId, offset, quantity);
     
    				// 控制台输出
    				for (int value : registerValues) {
    					System.out.println("Address: " + offset++ + ", Value: " + value);
    				}
     
    			} catch (ModbusProtocolException e) {
    				e.printStackTrace();
    			} catch (ModbusNumberException e) {
    				e.printStackTrace();
    			} catch (ModbusIOException e) {
    				e.printStackTrace();
    			} finally {
    				try {
    					master.disconnect();
    				} catch (ModbusIOException e) {
    					e.printStackTrace();
    				}
    			}
    		} catch (RuntimeException e) {
    			throw e;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    打印到控制台的信息

    Address: 0, Value: 88
    Address: 1, Value: 66
    Address: 2, Value: 8
    Address: 3, Value: 6
    Address: 4, Value: 32727
    Address: 5, Value: 32808
    Address: 6, Value: 0
    Address: 7, Value: 3
    Address: 8, Value: 2
    Address: 9, Value: 1
    

    使用modbus4j

    maven依赖

    • 官方说明:https://github.com/infiniteautomation/modbus4j
    • 有个坑:Maven配的阿里云仓库,下载不下来,注释掉阿里云仓库使用默认仓库才能下载好。

    pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.tcb</groupId>
      <artifactId>modbus</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>modbus</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
      
      <!-- 若想引用modbus4j需要引入下列repository id:ias-snapshots id:ias-releases 两个 ,使用默认仓库下载,不要使用阿里云仓库-->
        <repositories>
    	    <repository>
    	        <releases>
    	            <enabled>false</enabled>
    	        </releases>
    	        <snapshots>
    	            <enabled>true</enabled>
    	        </snapshots>
    	        <id>ias-snapshots</id>
    	        <name>Infinite Automation Snapshot Repository</name>
    	        <url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
    	    </repository>
    	    <repository>
    	        <releases>
    	            <enabled>true</enabled>
    	        </releases>
    	        <snapshots>
    	            <enabled>false</enabled>
    	        </snapshots>
    	        <id>ias-releases</id>
    	        <name>Infinite Automation Release Repository</name>
    	        <url>https://maven.mangoautomation.net/repository/ias-release/</url>
    	    </repository>
    	</repositories>
    	
        <dependencies>
    		 <dependency>
    		    <groupId>junit</groupId>
    		    <artifactId>junit</artifactId>
    		    <version>4.13-beta-3</version>
    		    <scope>test</scope>
    		</dependency>
    		<dependency>
    		    <groupId>com.infiniteautomation</groupId>
    		    <artifactId>modbus4j</artifactId>
    		    <version>3.0.3</version>
    		</dependency>
    		
    		<dependency>
    		    <groupId>org.apache.commons</groupId>
    		    <artifactId>commons-lang3</artifactId>
    		    <version>3.9</version>
    		</dependency>
        </dependencies>
    
    </project>
    
    

    Java实现modbus协议通讯

    原文链接:http://www.leftso.com/blog/83.html
    核心依赖:

    • modbus4j.jar
    • commons-lang3-3.0.jar

    Modbus4jUtils类

    package com.tcb.modbus;
    
    import com.serotonin.modbus4j.BatchRead;
    import com.serotonin.modbus4j.BatchResults;
    import com.serotonin.modbus4j.ModbusFactory;
    import com.serotonin.modbus4j.ModbusMaster;
    import com.serotonin.modbus4j.code.DataType;
    import com.serotonin.modbus4j.exception.ErrorResponseException;
    import com.serotonin.modbus4j.exception.ModbusInitException;
    import com.serotonin.modbus4j.exception.ModbusTransportException;
    import com.serotonin.modbus4j.ip.IpParameters;
    import com.serotonin.modbus4j.locator.BaseLocator;
    
    /**
     * modbus通讯工具类,采用modbus4j实现
     * 
     * @author lxq
     * @dependencies modbus4j-3.0.3.jar
     * @website https://github.com/infiniteautomation/modbus4j
     */
    public class Modbus4jUtils {
    	/**
    	 * 工厂。
    	 */
    	static ModbusFactory modbusFactory;
    	static {
    		if (modbusFactory == null) {
    			modbusFactory = new ModbusFactory();
    		}
    	}
    
    	/**
    	 * 获取master
    	 * 
    	 * @return
    	 * @throws ModbusInitException
    	 */
    	public static ModbusMaster getMaster() throws ModbusInitException {
    		IpParameters params = new IpParameters();
    		params.setHost("localhost");
    		params.setPort(502);
    		//
    		// modbusFactory.createRtuMaster(wapper); //RTU 协议
    		// modbusFactory.createUdpMaster(params);//UDP 协议
    		// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
    		ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议
    		master.init();
    
    		return master;
    	}
    
    	/**
    	 * 读取[01 Coil Status 0x]类型 开关数据
    	 * 
    	 * @param slaveId
    	 *            slaveId
    	 * @param offset
    	 *            位置
    	 * @return 读取值
    	 * @throws ModbusTransportException
    	 *             异常
    	 * @throws ErrorResponseException
    	 *             异常
    	 * @throws ModbusInitException
    	 *             异常
    	 */
    	public static Boolean readCoilStatus(int slaveId, int offset)
    			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    		// 01 Coil Status
    		BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
    		Boolean value = getMaster().getValue(loc);
    		return value;
    	}
    
    	/**
    	 * 读取[02 Input Status 1x]类型 开关数据
    	 * 
    	 * @param slaveId
    	 * @param offset
    	 * @return
    	 * @throws ModbusTransportException
    	 * @throws ErrorResponseException
    	 * @throws ModbusInitException
    	 */
    	public static Boolean readInputStatus(int slaveId, int offset)
    			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    		// 02 Input Status
    		BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
    		Boolean value = getMaster().getValue(loc);
    		return value;
    	}
    
    	/**
    	 * 读取[03 Holding Register类型 2x]模拟量数据
    	 * 
    	 * @param slaveId
    	 *            slave Id
    	 * @param offset
    	 *            位置
    	 * @param dataType
    	 *            数据类型,来自com.serotonin.modbus4j.code.DataType
    	 * @return
    	 * @throws ModbusTransportException
    	 *             异常
    	 * @throws ErrorResponseException
    	 *             异常
    	 * @throws ModbusInitException
    	 *             异常
    	 */
    	public static Number readHoldingRegister(int slaveId, int offset, int dataType)
    			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    		// 03 Holding Register类型数据读取
    		BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
    		Number value = getMaster().getValue(loc);
    		return value;
    	}
    
    	/**
    	 * 读取[04 Input Registers 3x]类型 模拟量数据
    	 * 
    	 * @param slaveId
    	 *            slaveId
    	 * @param offset
    	 *            位置
    	 * @param dataType
    	 *            数据类型,来自com.serotonin.modbus4j.code.DataType
    	 * @return 返回结果
    	 * @throws ModbusTransportException
    	 *             异常
    	 * @throws ErrorResponseException
    	 *             异常
    	 * @throws ModbusInitException
    	 *             异常
    	 */
    	public static Number readInputRegisters(int slaveId, int offset, int dataType)
    			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    		// 04 Input Registers类型数据读取
    		BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
    		Number value = getMaster().getValue(loc);
    		return value;
    	}
    
    	/**
    	 * 批量读取使用方法
    	 * 
    	 * @throws ModbusTransportException
    	 * @throws ErrorResponseException
    	 * @throws ModbusInitException
    	 */
    	public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    
    		BatchRead<Integer> batch = new BatchRead<Integer>();
    
    		batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
    		batch.addLocator(1, BaseLocator.inputStatus(1, 0));
    
    		ModbusMaster master = getMaster();
    
    		batch.setContiguousRequests(false);
    		BatchResults<Integer> results = master.send(batch);
    		System.out.println(results.getValue(0));
    		System.out.println(results.getValue(1));
    	}
    
    	/**
    	 * 测试
    	 * 
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		try {
    			// 01测试
    			Boolean v011 = readCoilStatus(1, 0);
    			Boolean v012 = readCoilStatus(1, 1);
    			Boolean v013 = readCoilStatus(1, 6);
    			System.out.println("v011:" + v011);
    			System.out.println("v012:" + v012);
    			System.out.println("v013:" + v013);
    			// 02测试
    			Boolean v021 = readInputStatus(1, 0);
    			Boolean v022 = readInputStatus(1, 1);
    			Boolean v023 = readInputStatus(1, 2);
    			System.out.println("v021:" + v021);
    			System.out.println("v022:" + v022);
    			System.out.println("v023:" + v023);
    
    			// 03测试
    			Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float
    			Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上
    			System.out.println("v031:" + v031);
    			System.out.println("v032:" + v032);
    
    			// 04测试
    			Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);//
    			Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);//
    			System.out.println("v041:" + v041);
    			System.out.println("v042:" + v042);
    			// 批量读取
    			batchRead();
    
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    代码理解

    slave配置

    输出信息

    v011:true
    v012:false
    v013:true
    v021:true
    v022:false
    v023:true
    v031:7.5
    v032:10.5
    v041:1.5
    v042:3.0
    7.5
    true
    

    Java通过modbus4j对数据的写入

    原文链接:http://www.leftso.com/blog/83.html

    类Modbus4jWriteUtils.java

    package com.tcb.modbus;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import com.serotonin.modbus4j.ModbusFactory;
    import com.serotonin.modbus4j.ModbusMaster;
    import com.serotonin.modbus4j.code.DataType;
    import com.serotonin.modbus4j.exception.ErrorResponseException;
    import com.serotonin.modbus4j.exception.ModbusInitException;
    import com.serotonin.modbus4j.exception.ModbusTransportException;
    import com.serotonin.modbus4j.ip.IpParameters;
    import com.serotonin.modbus4j.locator.BaseLocator;
    import com.serotonin.modbus4j.msg.ModbusResponse;
    import com.serotonin.modbus4j.msg.WriteCoilRequest;
    import com.serotonin.modbus4j.msg.WriteCoilResponse;
    import com.serotonin.modbus4j.msg.WriteCoilsRequest;
    import com.serotonin.modbus4j.msg.WriteCoilsResponse;
    import com.serotonin.modbus4j.msg.WriteRegisterRequest;
    import com.serotonin.modbus4j.msg.WriteRegisterResponse;
    import com.serotonin.modbus4j.msg.WriteRegistersRequest;
    
    /**
     * modbus4j写入数据
     * 
     * @author xq
     *
     */
    public class Modbus4jWriteUtils {
    	static Log log = LogFactory.getLog(Modbus4jWriteUtils.class);
    	/**
    	 * 工厂。
    	 */
    	static ModbusFactory modbusFactory;
    	static {
    		if (modbusFactory == null) {
    			modbusFactory = new ModbusFactory();
    		}
    	}
    
    	/**
    	 * 获取tcpMaster
    	 * 
    	 * @return
    	 * @throws ModbusInitException
    	 */
    	public static ModbusMaster getMaster() throws ModbusInitException {
    		IpParameters params = new IpParameters();
    		params.setHost("localhost");
    		params.setPort(502);
    
    		ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, false);
    		tcpMaster.init();
    
    		return tcpMaster;
    	}
    
    	/**
    	 * 写 [01 Coil Status(0x)]写一个 function ID = 5
    	 * 
    	 * @param slaveId
    	 *            slave的ID
    	 * @param writeOffset
    	 *            位置
    	 * @param writeValue
    	 *            值
    	 * @return 是否写入成功
    	 * @throws ModbusTransportException
    	 * @throws ModbusInitException
    	 */
    	public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
    			throws ModbusTransportException, ModbusInitException {
    		// 获取master
    		ModbusMaster tcpMaster = getMaster();
    		// 创建请求
    		WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
    		// 发送请求并获取响应对象
    		WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
    		if (response.isException()) {
    			return false;
    		} else {
    			return true;
    		}
    	}
    
    	/**
    	 * 写[01 Coil Status(0x)] 写多个 function ID = 15
    	 * 
    	 * @param slaveId
    	 *            slaveId
    	 * @param startOffset
    	 *            开始位置
    	 * @param bdata
    	 *            写入的数据
    	 * @return 是否写入成功
    	 * @throws ModbusTransportException
    	 * @throws ModbusInitException
    	 */
    	public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
    			throws ModbusTransportException, ModbusInitException {
    		// 获取master
    		ModbusMaster tcpMaster = getMaster();
    		// 创建请求
    		WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
    		// 发送请求并获取响应对象
    		WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
    		if (response.isException()) {
    			return false;
    		} else {
    			return true;
    		}
    
    	}
    
    	/***
    	 * 写[03 Holding Register(4x)] 写一个 function ID = 6
    	 * 
    	 * @param slaveId
    	 * @param writeOffset
    	 * @param writeValue
    	 * @return
    	 * @throws ModbusTransportException
    	 * @throws ModbusInitException
    	 */
    	public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)
    			throws ModbusTransportException, ModbusInitException {
    		// 获取master
    		ModbusMaster tcpMaster = getMaster();
    		// 创建请求对象
    		WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
    		WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
    		if (response.isException()) {
    			log.error(response.getExceptionMessage());
    			return false;
    		} else {
    			return true;
    		}
    
    	}
    
    	/**
    	 * 
    	 * 写入[03 Holding Register(4x)]写多个 function ID=16
    	 * 
    	 * @param slaveId
    	 *            modbus的slaveID
    	 * @param startOffset
    	 *            起始位置偏移量值
    	 * @param sdata
    	 *            写入的数据
    	 * @return 返回是否写入成功
    	 * @throws ModbusTransportException
    	 * @throws ModbusInitException
    	 */
    	public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)
    			throws ModbusTransportException, ModbusInitException {
    		// 获取master
    		ModbusMaster tcpMaster = getMaster();
    		// 创建请求对象
    		WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
    		// 发送请求并获取响应对象
    		ModbusResponse response = tcpMaster.send(request);
    		if (response.isException()) {
    			log.error(response.getExceptionMessage());
    			return false;
    		} else {
    			return true;
    		}
    	}
    
    	/**
    	 * 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
    	 * 
    	 * @param slaveId
    	 * @param offset
    	 * @param value
    	 *            写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
    	 * @param registerCount
    	 *            ,com.serotonin.modbus4j.code.DataType
    	 * @throws ModbusTransportException
    	 * @throws ErrorResponseException
    	 * @throws ModbusInitException
    	 */
    	public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)
    			throws ModbusTransportException, ErrorResponseException, ModbusInitException {
    		// 获取master
    		ModbusMaster tcpMaster = getMaster();
    		// 类型
    		BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
    		tcpMaster.setValue(locator, value);
    	}
    
    	public static void main(String[] args) {
    		try {
    			//@formatter:off
    			// 测试01
    //			boolean t01 = writeCoil(1, 0, true);
    //			System.out.println("T01:" + t01);
    
    			// 测试02
    //			boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });
    //			System.out.println("T02:" + t02);
    
    			// 测试03
    //			short v = -3;
    //			boolean t03 = writeRegister(1, 0, v);
    //			System.out.println("T03:" + t03);
    			// 测试04
    //			boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });
    //			System.out.println("t04:" + t04);
    			//写模拟量
    			writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT);
    			
    			//@formatter:on
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    	}
    
    }
    

    代码理解

    使用modbus-master-tcp

    原文链接:http://www.leftso.com/blog/310.html
    源码地址:https://github.com/digitalpetri/modbus
    modbus tcp通讯Java的方案之前已经讲解过一种,modbus4j实现Java语言的modbus tcp协议通讯。从上一个方案中我们不难发现modbus4j的通讯实现方式是同步的。
    实际应用中可能会读取大量的数据。同步处理对于应用的响应还是不太友好的。
    本博客主要讲解另外一种Java语言的modbux tcp通讯方案。那就是modbus-master-tcp。

    maven依赖

    pom.xml注意,需要将java的编译版本指定到1.8.因为只有1.8以后才支持lambda表达式。

    	<dependency>
    		<groupId>com.digitalpetri.modbus</groupId>
    		<artifactId>modbus-master-tcp</artifactId>
    		<version>1.1.0</version>
    	</dependency>
    

    观察可以发现,modbus-master-tcp项目的底层是基于netty框架开发。天然的支持异步处理。在性能方面有很好的提升。

    编写modbus tcp读取案例

    类SimpleMasterExample

    package com.ioufev;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    import com.digitalpetri.modbus.codec.Modbus;
    import com.digitalpetri.modbus.master.ModbusTcpMaster;
    import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
    import com.digitalpetri.modbus.requests.ReadCoilsRequest;
    import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
    import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
    import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
    import com.digitalpetri.modbus.responses.ReadCoilsResponse;
    import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
    import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
    import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.util.ReferenceCountUtil;
    
    /***
     * modbus TCP协议Java通讯读取例子
     *
     *
     */
    public class ModbusMasterTCPDemo {
    
        static ModbusTcpMaster master;
    
        /**
         * 获取TCP协议的Master
         *
         * @return
         */
        public static void initModbusTcpMaster() {
            if (master == null) {
                // 创建配置
                ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").setPort(502).build();
                master = new ModbusTcpMaster(config);
            }
        }
    
        /***
         * 释放资源
         */
        public static void release() {
            if (master != null) {
                master.disconnect();
            }
            Modbus.releaseSharedResources();
        }
    
        /**
         * 读取Coils开关量
         *
         * @param address
         *            寄存器开始地址
         * @param quantity
         *            数量
         * @param unitId
         *            ID
         * @return 读取值
         * @throws InterruptedException
         *             异常
         * @throws ExecutionException
         *             异常
         */
        public static Boolean readCoils(int address, int quantity, int unitId)
                throws InterruptedException, ExecutionException {
            Boolean result = null;
            CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity),
                    unitId);
            ReadCoilsResponse readCoilsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
            if (readCoilsResponse != null) {
                ByteBuf buf = readCoilsResponse.getCoilStatus();
                result = buf.readBoolean();
                ReferenceCountUtil.release(readCoilsResponse);
            }
            return result;
        }
    
        /**
         * 读取readDiscreteInputs开关量
         *
         * @param address
         *            寄存器开始地址
         * @param quantity
         *            数量
         * @param unitId
         *            ID
         * @return 读取值
         * @throws InterruptedException
         *             异常
         * @throws ExecutionException
         *             异常
         */
        public static Boolean readDiscreteInputs(int address, int quantity, int unitId)
                throws InterruptedException, ExecutionException {
            Boolean result = null;
            CompletableFuture<ReadDiscreteInputsResponse> future = master
                    .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId);
            ReadDiscreteInputsResponse discreteInputsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
            if (discreteInputsResponse != null) {
                ByteBuf buf = discreteInputsResponse.getInputStatus();
                result = buf.readBoolean();
                ReferenceCountUtil.release(discreteInputsResponse);
            }
            return result;
        }
    
        /**
         * 读取HoldingRegister数据
         *
         * @param address
         *            寄存器地址
         * @param quantity
         *            寄存器数量
         * @param unitId
         *            id
         * @return 读取结果
         * @throws InterruptedException
         *             异常
         * @throws ExecutionException
         *             异常
         */
        public static Number readHoldingRegisters(int address, int quantity, int unitId)
                throws InterruptedException, ExecutionException {
            Number result = null;
            CompletableFuture<ReadHoldingRegistersResponse> future = master
                    .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);
            ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
            if (readHoldingRegistersResponse != null) {
                ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                result = buf.readFloat();
                ReferenceCountUtil.release(readHoldingRegistersResponse);
            }
            return result;
        }
    
        /**
         * 读取InputRegisters模拟量数据
         *
         * @param address
         *            寄存器开始地址
         * @param quantity
         *            数量
         * @param unitId
         *            ID
         * @return 读取值
         * @throws InterruptedException
         *             异常
         * @throws ExecutionException
         *             异常
         */
        public static Number readInputRegisters(int address, int quantity, int unitId)
                throws InterruptedException, ExecutionException {
            Number result = null;
            CompletableFuture<ReadInputRegistersResponse> future = master
                    .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);
            ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
            if (readInputRegistersResponse != null) {
                ByteBuf buf = readInputRegistersResponse.getRegisters();
                result = buf.readDouble();
                ReferenceCountUtil.release(readInputRegistersResponse);
            }
            return result;
        }
    
        public static void main(String[] args) {
            try {
                // 初始化资源
                initModbusTcpMaster();
    
                // 执行操作
    
                // 读取开关量
                System.out.println(readCoils(0, 1, 1));
                System.out.println(readDiscreteInputs(0, 1, 1));
                System.out.println(readDiscreteInputs(1, 1, 1));
    
                // 读取模拟量
                System.out.println(readHoldingRegisters(0, 2, 1));
                System.out.println(readHoldingRegisters(2, 2, 1));
                System.out.println(readHoldingRegisters(4, 2, 1));
                System.out.println(readInputRegisters(2, 4, 1));
                System.out.println(readInputRegisters(6, 4, 1));
    
                // 释放资源
                release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    编写modbus tcp写入案例

    0x06 Write Single Register

    功能码06 写入单个寄存器

    类WriteSingleRegisterRequest

    // 发送单个寄存器数据,一般是无符号16位值:比如10
    master.sendRequest(new WriteSingleRegisterRequest(address, value), unitId);
    

    0x10 Write Multiple Registers

    功能码10 写入多个寄存器

    写入多个寄存器

    类WriteMultipleRegistersRequest

    // float类型转字节数组
    byte[] bytes = float2bytes(values);
    // 转netty需要的字节类型
    ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
    
    // 发送多个寄存器数据,数据类型由quantity决定,2是float类型,4是double类型
    master.sendRequest(new WriteMultipleRegistersRequest(address,quantity,byteBuf), unitId);
    

    代码理解

    slave:和上面的一样

    输出信息

    true
    false
    false
    10.1
    -5.6
    9.2
    6.00002
    -90.122222
    

    评价感受

    • jlibmodbus:集成多个串口通信开源库,有意思
    • modbus4j:很有名
    • modbus-master-tcp:底层netty,支持异步
    • Jamod:Github上安卓开发modbus通信用的多
    展开全文
  • TCP滑动窗口

    千次阅读 2021-02-18 15:13:39
    TCP滑动窗口 参考 https://blog.csdn.net/yao5hed/article/details/81046945 https://blog.csdn.net/ligupeng7929/article/details/79597423 https://blog.csdn.net/h2604396739/article/details/85239439 概述 滑动...

    TCP滑动窗口

    参考

    https://blog.csdn.net/yao5hed/article/details/81046945

    https://blog.csdn.net/ligupeng7929/article/details/79597423

    https://blog.csdn.net/h2604396739/article/details/85239439

    概述

    滑动窗口实现了TCP流控制。首先明确滑动窗口的范畴:TCP是双工的协议,会话的双方都可以同时接收和发送数据。TCP会话的双方都各自维护一个发送窗口和一个接收窗口。各自的接收窗口大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。各自的发送窗口则要求取决于对端通告的接收窗口,要求相同。

    滑动窗口解决的是流量控制的的问题,就是如果接收端和发送端对数据包的处理速度不同,如何让双方达成一致。接收端的缓存传输数据给应用层,但这个过程不一定是即时的,如果发送速度太快,会出现接收端数据overflow,流量控制解决的是这个问题。

    滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小(接收方可以接受缓冲区大小),从而控制发送方的发送速度,不过如果接收端的缓冲区一旦面临数据溢出,窗口大小值也会随之被设置一个更小的值通知给发送端,从而控制数据发送量(发送端会根据接收端指示,进行流量控制)

    当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。

    窗口的概念

    发送方的发送缓存内的数据都可以被分为4类:

    1. 已发送,已收到ACK
    2. 已发送,未收到ACK
    3. 未发送,但允许发送
    4. 未发送,但不允许发送

    其中类型2和3都属于发送窗口。

    接收方的缓存数据分为3类:
    \1. 已接收
    \2. 未接收但准备接收
    \3. 未接收而且不准备接收

    其中类型2属于接收窗口。

    窗口大小代表了设备一次能从对端处理多少数据,之后再传给应用层。缓存传给应用层的数据不能是乱序的,窗口机制保证了这一点。现实中,应用层可能无法立刻从缓存中读取数据。

    滑动机制

    1. 发送窗口只有收到发送窗口内字节的ACK确认,才会移动发送窗口的左边界。

    2. 接收窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。以此确保对端会对这些数据重传。同时实行累计确认,例如:

      发送端发了ABCD,接收端收到了ABD,因为没有收到C,在收到D时只会返回B的ACK(B的ACK内容时C,代表了下一次希望收到的),
      若发送了ABCD全收到,可以只返回D,代表D和D之前的全部收到了

    3. 遵循快速重传、累计确认、选择确认等规则。

    4. 发送方发的window size = 8192;就是接收端最多发送8192字节,这个8192一般就是发送方接收缓存的大小。

    窗口大小

    img
    这里我们可以看到假设窗口的大小是1,也是就每次只能发送一个数据,并且发送方只有接受方对这个数据进行确认了以后才能发送下一个数据。 所以就会存在如下两个问题:

    1. 如果说窗口过小,那么当传输比较大的数据的时候需要不停的对数据进行确认,这个时候就会造成很大的延迟。
    2. 如果说窗口的大小定义的过大。我们假设发送方一次发送100个数据。但是接收方只能处理50个数据。这样每次都会只对这50个数据进行确认。发送方下一次还是发送100个数据,但是接受方还是只能处理50个数据。这样就有不必要的数据来拥塞我们的链路。

    所以我们就引入了滑动窗口机制,窗口的大小并不是固定的而是根据我们之间的链路的带宽的大小、链路是否拥护塞、接受方是否能处理这么多数据,三个元素共同决定。

    img

    首先是第一次发送数据这个时候的窗口大小是根据链路带宽的大小来决定的。我们假设这个时候窗口的大小是3。这个时候接受方收到数据以后会对数据进行确认告诉发送方我下次希望手到的是数据是多少。这里我们看到接收方发送的ACK=3(这是发送方发送序列2的回答确认,下一次接收方期望接收到的是3序列信号)。这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方只收到了2个。就知道第3个数据对方没有收到。下次在发送的时候就从第3个数据开始发。这个时候窗口大小就变成了2
    img

    这个时候发送方发送2个数据。

    img

    看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的2个数据对方收了这个时候开始发送第5个数据。
    这就是滑动窗口的工作机制,当链路变好了或者变差了这个窗口还会发生变话,并不是第一次协商好了以后就永远不变了。

    模拟动画

    模拟特点

    找到了一个模拟TCP窗口发送的动画的地址,稍微有缺陷:1. 丢包率如果设得太高,有时无论重发多少次都不能恢复正常 2. 窗口最大可为10,其实应该为9

    明确发送端和接收端,发送A~S数据包,我们不会从头到尾分析,因为过程比较长。
    \1. 简化了窗口大小,双方窗口大小都一直是4
    \2. 设置一定的丢包率,否则没什么值得分析的,包括sender发送的数据包和receiver回复的ACK包。
    \3. 简化重传机制,出现丢包则直接重传,不等3个冗余ACK和超时。
    \4. 既不是选择重传也不是退回N步,重传的包是随机的

    分析滑动窗口机制

    1. 首先发送端发送A,B,C,D四个包,但是A,B丢失,只有C,D到达接收端。
      img
    2. 接收端没有收到A,所以不回复ACK包。发送端重传A,B,C,D四个包,这次全都到达了。
      img
    3. 接收端先获得A,发ACK包A,但是中途丢失;获得B后,根据累计确认的原则,发D的ACK包,然后窗口滑动。再次获得C,D后,连续回复2个D的ACK包,其中C对应的ACK包丢失。
      img
    4. 发送端连收2个D的ACK包,说明4个包对方都已收到,窗口滑动,发E,F,G,H包,其中G包丢失。现在整个序列的状态:ABCD是已发送已确认,EFGH是已发送未确认,I~S是不能发送。
      img
    5. 接收端先收到E,发ACK包;收到F后发F的ACK包;未收到G,还是发F的ACK包;收到H,还是发F的ACK包。不幸的是,三个ACK包全都丢失。
      img
    6. 发送端收到E的ACK包,窗口向右滑动一位;然后再发送F,G,H,I,其中F丢失。
      img
    7. 接收端获得I,因为没有G,只好回复F的ACK包。相继收到G,H包。
      img
    8. 接收端根据累计确认,连发两个I包,其中H对应的丢失。窗口向右滑动。
      img
    9. 发送端接收I的ACK包后,向右滑动四位。发送J,K,L,M四个包,后面不再分析。
      img

    从上面的过程中,我们可以得到以下结论:
    \1. TCP连接是通过数据包和ACK实现的,我们作为第三者可以看到双方发包的过程,但接受者在收到之前不知道发送方发的是什么,同样的,发送方在收到ACK前也不知道对方是否成功接收。

    1. 发送方没有收到接收方发回的ACK,就不能向右滑动。假设发送方向接收方发了ABCD就滑动,只要对方没收到A,就不能滑动,那么就会出现二者不同步的局面。
    2. 滑动窗口提高了信道利用率,TCP是发送报文段为单位的,假如每发一个报文就要等ACK,那么对于大数据包,等待时间就太长了。只要发送的报文在滑动窗口里面,不用等每个ACK回来就可以向右滑动。本例中,开始接收端空着AB,只有CD,此时不能滑动;之后接收到EF和H,直接向右滑动2位,不必等G到位。
    3. 窗口大小不能大于序号空间大小的一半。目的是为了不让两个窗口出现交迭,比如总大小为7,窗口大小都为4,接收窗口应当滑动4,但只剩3个序号,导致两个窗口交迭。
    4. 有一种情况没出现:发送方发ABCD,接收方都收到然后向右滑动,但回复的ACK包全丢了。发送方未收到任何ACK, timeout后会重发ABCD,此时的接收方按累计确认的原则,收到ABCD后只会重发D的ACK,发送方收到后向右滑动。

    对比滑动窗口和拥塞窗口

    滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,用于流量控制。拥塞窗口是控制发送速率的,避免发的过多,发送端使用。因为tcp是全双工,所以两边都有滑动窗口。
    两个窗口的维护是独立的,滑动窗口主要由接收方反馈缓存情况来维护,拥塞窗口主要由发送方的拥塞控制算法检测出的网络拥塞程度来决定的。

    拥塞窗口控制sender向connection传输数据的速率,使这个速率为网络拥堵状况的函数。

    展开全文
  • JAVA [ TCP/IP ]

    千次阅读 2011-07-27 19:57:12
    TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议.TCP/IP 的工作原理 -->本文采用TCP/IP协议传送文件为例,说明TCP/IP的工作原理,其中应用层传输文件采用文件传输协议(FTP)

     应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。 
      传输层:在此层中,它提供了节点间的数据传送,应用程序之间的通信服务,主要功能是数据格式化、数据确认和丢失重传等。如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。 
      连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)


    TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议.

    TCP/IP 的工作原理 -->本文采用TCP/IP协议传送文件为例,说明TCP/IP的工作原理,其中应用层传输文件采用文件传输协议(FTP)。 

    TCP/IP协议的工作流程如下: 
    ●在源主机上,应用层将一串应用数据流传送给传输层。 
    ●传输层将应用层的数据流截成分组,并加上TCP报头形成TCP段,送交网络层。 
    ●在网络层给TCP段加上包括源、目的主机IP地址的IP报头,生成一个IP数据包,并将IP数据包送交链路层。 
    ●链路层在其MAC帧的数据部分装上IP数据包,再加上源、目的主机的MAC地址和帧头,并根据其目的MAC地址,将MAC帧发往目的主机或IP路由器。
    ●在目的主机,链路层将MAC帧的帧头去掉,并将IP数据包送交网络层。 
    ●网络层检查IP报头,如果报头中校验和与计算结果不一致,则丢弃该IP数据包;若校验和与计算结果一致,则去掉IP报头,将TCP段送交传输层。 
    ●传输层检查顺序号,判断是否是正确的TCP分组,然后检查TCP报头数据。若正确,则向源主机发确认信息;若不正确或丢包,则向源主机要求重发信息。 
    ●在目的主机,传输层去掉TCP报头,将排好顺序的分组组成应用数据流送给应用程序。这样目的主机接收到的来自源主机的字节流,就像是直接接收来自源主机的字节流一样。 

    /**
     * 127.0.0.1没有于网卡绑定,当网卡坏了时,仍可测试使用本地网络程序
     * IP地址保证数据传送到计算机,但是不能确定是哪个网络程序,需要使用PORT,两个字节的整数
     * PORT 0~1023用于知名的网络服务和应用,端口号在0~65535之间
     * 
     * 高级协议 UDP TCP
     *  TCP:传输控制协议,面向连接的通信协议,打电话
     *  UDP:用户数据报协议,一个主机向另一个主机发送,它不管另个主机是否启动,另一个主机接收到信息后不会回应,像是传呼台,一般发送几遍
     *  
     *  数据帧格式
     *  协议类型 | 源IP | 目标IP | 源端口 | 目标端口 | 帧序号 | 帧数据
     *   1 hello
     *   2   world
     * Socket是网络驱动层提供给应用程序编程的接口和一种机制
     * 类似港口码头,应用程序    把       货物       放到港口码头,完成了货物的运货
     * 应用程序 只需等待       货物      到达码头,将        货物取走
     *  
     *  
     *   1 产生Socket
     *                      应用程序-----------------------------
     *                       | ^ |
     *                      2 | | |
     *                       | | |
     *            调用bind将   | |  4应用程序从Socket中取数据  |
     *            Socket的信息 | --------------------------- Socket
     *            通知给驱动程序 | -------------------------->
     *                       | |     3 驱动程序根据从网卡传送来的
     *                       | | 数据包中指定目标端口号,将处理后的数据传送到
     *                       | |         相应的socket中
     *                          | |
     *                      驱动程序
     *  
     *  ServerSocket 用于TCP通信的服务器
     *  DatagramDocket类用于UDP通信
     *  Scoket类用于TCP通信的服务器和客户端
     *  
     */
    public class InetAddressExample {
    public static void main(String[] args){
    try{
    //该主机每一个接口所以对应的NetworkInterface类实例
    Enumeration<NetworkInterface> interfaceList = NetworkInterface.getNetworkInterfaces();
    if(interfaceList == null){
    System.out.println("--NO interfaces found--");
    }else{
    while(interfaceList.hasMoreElements()){
    NetworkInterface iface = interfaceList.nextElement();
    //返回一个本地名称
    System.out.println("Interface " + iface.getName() + ":");
    //获取与接口关联的地址
    Enumeration<InetAddress> addrList = iface.getInetAddresses();
    if(!addrList.hasMoreElements()){
    System.out.println("\t(No addresses for this interface)");
    }
    while(addrList.hasMoreElements()){
    InetAddress address = addrList.nextElement();
    System.out.println("\tAddress"
    + ((address instanceof Inet4Address? "(v4)"
    : (address instanceof Inet6Address? "(v6)" : "?"))));
    System.out.println("\t\t:" + address.getHostName());
    System.out.println("\t\t:" + address.getHostAddress());
    }
    }
    }
    }catch(SocketException se){
    System.out.println("Error getting network interfaces:" + se.getMessage());
    }
    }
    }

    /**
     * 
     *  TCP网络程序的工作原理
     *   UDP么有主从之分,两个可以是完全应用实例
     *  
     *   为Client创建的Socket
     *   两个Socket之间建立专线连接 服务器接受请求并创建Socket
     *   | ^
     *   | 客户端发出连接 |
     *  Client Socket [simple phone]  -------------------------------> ServerSockt  [114]
     *  
     *  ServerSocket类
     *  public ServerSocket()
     *  public ServerSocket(int port) 
     *  public ServerSocket(int port, int backlog)  //保持连接请求的客户数量
     *  public ServerSocket(int port, int backlog, InetAddress bindAddr)
     *  close()
     *  accpet() //返回建立专线连接的对象
     *  
     *  
     *  Socket类
     *  public Socket()  //轮循
     *  public Socket(String host, int port)
     *  public Socket(InetAddress address, int port)
     *  public Socket(String host, int port, InetAddress address, int localport)  
     *  //客户机有多快网卡,通过哪块网卡连接服务器
     *  
     *  网络字节流形式,数据交换
     *  getInputStream和getOutputStream
     *  
     *  简单的TCP服务器程序
     *   TCP服务器程序先运行,客户端才能连接上
     *   使用Windows提供的telnet程序测试
     *   使用BufferedReader包装类,从网络输入流中一次读取一行文本
     *   如何打开telent程序的本地回显文本
     * 
     * @author ShenJie
     *
     */

    public class TCPDemo1 {
    public static void main(String[]args) throws Exception{
    ServerSocket ss = new ServerSocket(8001);
    Socket s = ss.accept();
    InputStream ips = s.getInputStream();
    OutputStream ops = s.getOutputStream();

    ops.write("welcome to SHENJIE".getBytes());

    // byte[] buf = new byte[1024];
    // int len = ips.read(buf);
    // System.out.println(new String(buf, 0 , len));


    BufferedReader br = new BufferedReader(new InputStreamReader(ips));
    System.out.println(br.readLine());
    br.close();

    //关闭顺序
    ips.close();
    ops.close();
    s.close();
    ss.close();

    //用telnet实验
    //telnet localhost 8001
    //telnet > ?
    //telnet > set ?

    }
    }

    /**
     *  
     *  
     *  完善的TCP服务器程序模型
     *   同时与多个客户端会话,
     *   需要循环调用accept方法,会话不鞥你影响,需要独立运行
     *   客户端每次向服务器发送一行字符文本,服务器将这行字符文本中的所有字符反向排列送回客户端
     *   当客户端发送“quit”时,服务器结束与客户端的会话
     *  
     *  TCP客户端
     * 
     * @author ShenJie
     *
     */

    public class TCPDemo2 {
    public static void main(String[] args) throws Exception{
    ServerSocket ss = new ServerSocket(8001);
    boolean bRunning = true;
    //会话过程
    while(bRunning){
    Socket s = ss.accept();
    //每个会话启动一个线程
    new Thread(new Servicer(s)).start();
    }
    ss.close();
    }
    }
    class Servicer implements Runnable{
    Socket s;
    public Servicer(Socket s){
    this.s = s;
    }
    @Override
    public void run() {
    InputStream ips;
    try {
    ips = s.getInputStream();
    OutputStream ops = s.getOutputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(ips));
    //true自动刷新缓冲区
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(ops),true);
    //PrintStream 都是产生\n
    while(true){
    String strLine = br.readLine();
    if(strLine.equalsIgnoreCase("quit")){
    break;
    }
    System.out.println(strLine + strLine.length());
    //abd{backspace}c
    //c{backspace}dba
    //abc --- > abd

    String strEcho  = new StringBuffer(strLine).reverse().toString();
    pw.println(strLine + " ----> " + strEcho);
    }
    br.close();
    pw.close();
    s.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
     * 
     *  如何检测和解决端口冲突问题
     *   netstat
     *  通过TCP程序在网络上传送对象
     * @author ShenJie
     */

    public class TCPDemo3 {
    public static void main(String[] args) throws Exception, IOException{
    Socket s = new Socket("localhost", 9999);
    InputStream ips = s.getInputStream();
    OutputStream ops = s.getOutputStream();

    BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
    //true自动刷新缓冲区
    PrintWriter pw = new PrintWriter(ops,true);

    BufferedReader brKeyBoard = new BufferedReader(new InputStreamReader(System.in));
    while(true){
    String strWord = brKeyBoard.readLine();
    pw.println(strWord);
    if(strWord.equalsIgnoreCase("quit")){
    break;
    }
    System.out.println(brNet.readLine());
    }
    pw.close();
    brNet.close();
    ops.close();
    ips.close();
    s.close();
    }
    }

    /**
     * ObjectInputStream 和 ObjectOutputStream从底层输入流中读取对象类型,以及写入
     * 
     * 应用通信协议和网络通信协议的关系
     * 
     * 电话系统tcp 语言ftp
     * 
     * @author ShenJie
     *
     */

    public class TCPDemo4 {
    public static void main(String[] args) throws Exception{
    ServerSocket ss =new ServerSocket(9999);
    Socket s = ss.accept();
    OutputStream ops = s.getOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(ops);
    Student stu = new Student(19,"wangwu", 22 ,"hunan");
    oos.writeObject(stu);
    oos.close();
    ops.close();
    s.close();
    ss.close();
    }
    }
    class ObjectClient{
    public ObjectClient() throws Exception{
    Socket s = new Socket("localhost", 9999);
    InputStream ips = s.getInputStream();
    ObjectInputStream ois = new ObjectInputStream(ips);

    Student stu = (Student)ois.readObject();
    System.out.print("id is =" + stu.id);
    System.out.print("name is =" + stu.name);
    System.out.print("age is =" + stu.age);
    System.out.print("department is =" + stu.department);

    ois.close();
    ips.close();
    s.close();
    }
    }
    class Student implements Serializable{
    int id;
    String name;
    int age;
    String department;

    public Student(int id, String name, int age, String department){
    this.id =id; this.name =name; this.age= age; this.department=department;
    }
    }

    ****************************************

    int recvMsgSize;
    byte[] reveiveBuf = new byte[BUFSIZE];

    //接收并复制数据,直到客户端关闭
    while((recvMsgSize = in.read(reveiveBuf)) != -1){
    out.write(reveiveBuf,0,recvMsgSize);
    }

    ***************************************************

    /**

    *  简易 TCPEchoServerPool

    */

    public class TCPEchoServerPool {
    private static final int BUFSIZE =32;
    public static void main(String[] args) throws IOException{
    int servPort = 9999;
    int threadPoolSize = 20;
    final ServerSocket servSock = new ServerSocket(servPort);

    for(int i=0;i<threadPoolSize;i++){
    Thread thread = new Thread(){
    public void run(){
    while(true){
    try{
    Socket clntSock = servSock.accept();
    //handleEchoClient
    }catch(IOException e){

    }
    }
    }
    };
    thread.start();
    }
    }
    }



    /**
     * 港口码头
     * public DatagramSocket() 系统自动分配端口号:例如给别人打电话,自己的号码不固定
     * public DatagramSocket(int port)  :例如要朋友给自己打电话
     * public DatagramSocket(int port, InetAddress Iaddr)   多个ip地址上运行,明确指定发送接收的IP地址
     * 
     * 接收和发送数据的集装箱
     * public DatagramPacket(byte[] buf,int length)   接收数据
     * public DatagramPacket(byte[] buf,int length, InetAddress address,int port)  发送数据,需要有接收方的地址信息 
     * 
     * getInetAddress 和 getPort方法可以获得发送方的地址信息
     * getData 和 getLength方法  == packet中字节数组缓冲区,packet中实际收到的数据长度
     * 
     * 数据包定位1024个字节,发送不能多于这个
     * 
     * InetAddress是表示计算机ip地址的一个类,一般计算机地址用"192.168.0.1"和"www.it315.org"表示
     * getByName方法返回一个InetAddress实例对象
     * getHostAddress返回 类似"192.168.0.1"的地址
     * 
     * 字符串与字节数组之间双向转换
     * UDP接收程序先启动运行,才能接收UDP发送程序发送的数据
     * CMD用start命令打开新命令行窗口的好处,继承环境
     * 解决发送中文字符串的问题
     * 
     * @author ShenJie
     *
     */

    public class UDPDemo1 {
    public static void main(String[] args){
    }
    }
    //数据一发完就结束了
    class UdpSend{
    public static void main(String[] args){
    DatagramSocket ds = null;
    try {
    ds = new DatagramSocket();
    String strInfo = "Hello, this is Jesse!";
    // String strInfo = "用中文的字符!";
    try {
    ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.length(),3000));
    // 中文时
    // ds.send(new DatagramPacket(strInfo.getBytes(),strInfo.getBytes().length(),3000));

    } catch (IOException e) {
    e.printStackTrace();
    }
    ds.close();
    } catch (SocketException e) {
    e.printStackTrace();
    }
    }
    }
    //接收程序开着,一直阻塞
    class UdpRecv{
    public static void main(String[] args){
    DatagramSocket ds = null;
    try {
    ds = new DatagramSocket(3000);
    byte[] buf = new byte[1024];
    DatagramPacket dp = new DatagramPacket(buf, 1024);
    ds.receive(dp);
    //接收实际收到的数据个数
    dp.getAddress();//获得对方的发送ip地址
    System.out.println(new String(dp.getData(),0,dp.getLength())
    + "from " + dp.getAddress().getHostAddress() + ":" + dp.getPort());
    ds.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
     * 多线程和GUI
     * 1. 编写图形用户界面
     * 2. 编写网络消息发送功能
     * 3. 编写网络消息接收功能
     *
     *  只有udp才能发送和接收广播地址
     *  广播地址需要计算
     *  可以放在INternet上使用,每台计算机需要有合法的IP地址,本来就能合法访问
     *  
     *  *******************************************************
     *  使用私有IP并通过网关代理上网的计算机不能
     *  
     *    192.168.0.2
     *        192.168.0.3  [内部网络没有合法地址]
     *        192.168.0.4
     *        192.168.0.5
     *                  ----------------------------交换机----------------------------
     *                   电话,isdn,cable,光纤专线连接
     *                   获得合法有效的IP地址 166.111.111.10
     *                   可以让Internet计算机访问到他
     *                   如果打开IP网络转发功能,内部网络其他计算机网关地址设置为这台机器的内部网络地址
     *                      |
     *                   |
     *                   |
     *                   |
     *                   internet-------------------------> 221.201.121.57
     *                  
     *   192.168.0.2 发送到  221.201.121.57, 先将数据发送到网关上
     *  数据格式为
     *     1.     192.168.0.2      221.201.121.57  3000   3000   hello
     *     2.     166.111.111.10   221.201.121.57  1027   3000   hello
     *     3.     221.201.121.57   166.111.111.10  3000   1027   world
     *     4.     221.201.121.57   192.168.0.2     3000   3000   world
     *     
     *     
     *  转发映射记录表
     *       192.168.0.2/3000    166.111.111.10/1027
     *       192.168.0.3/3000    166.111.111.10/1028
     *       
     *  *******************************************************
     */

    public class UDPDemo2 extends JFrame{
    List lst = new List();
    JTextField tfIP = new JTextField(15);
    JTextField tfData = new JTextField(20);
    DatagramSocket ds = null;

    public UDPDemo2(){
    try {
    ds =new DatagramSocket(3000);
    } catch (SocketException e) {
    e.printStackTrace();
    }
    this.add(lst, "Center");
    Panel p =new Panel();
    add(p, "South");

    p.setLayout(new BorderLayout());
    p.add(tfIP,"West");
    p.add(tfData,"East");

    //线程类接收网络上发送的消息
    new Thread(new Runnable(){
    @Override
    public void run() {
    byte[]  buf = new byte[1024];
    DatagramPacket dp = new DatagramPacket(buf, 1024);
    while(true){
    try {
    ds.receive(dp);
    } catch (IOException e) {
    /**
    * 当关闭的时候不发生异常
    */

    if(!ds.isClosed())
    e.printStackTrace();
    }
    //lst一般显示在最后一行,希望显示在第一行
    // lst.add("");

    lst.add(new String(/*dp.getData()*/buf, 0 , dp.getLength())
    + " from " + dp.getAddress().getHostAddress() + ":" + dp.getPort(),0);
    }
    }

    }).start();

    tfData.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
    byte[] buf = tfData.getText().getBytes();
    DatagramPacket dp;
    try {
    dp = new DatagramPacket(buf, buf.length,
    InetAddress.getByName(tfIP.getText()), 3000);
    try {
    ds.send(dp);
    } catch (IOException ex) {
    ex.printStackTrace();
    }
    } catch (UnknownHostException ex) {
    ex.printStackTrace();
    }
    tfData.setText("");
    }
    });
    addWindowListener(new WindowAdapter(){
    public void windowClosing(WindowEvent e){
    ds.close();
    dispose();
    System.exit(0);
    }
    });
    setTitle("UDP\u5355\u673a\u7f51\u7edc\u804a\u5929\u5de5\u5177");
    }
    public static void main(String[] args){
    UDPDemo2 ud = new UDPDemo2();
    ud.setSize(380,500);
    ud.setVisible(true);
    }
    }

    展开全文
  • JAVA基于TCP和UDP的网络连接

    千次阅读 2016-03-29 08:59:34
    TCP:需要经历“三次握手”建立连接后,发送数据客户端:public class ClientSocket { public static void main(String[] args) throws Exception { Socket so = new Socket("localhost", 88); // 创建Socket用户...
  • 效果图:李四先进入聊天室,张三也在,然后王五加入;...服务器端:package testTCP; import java.net.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.E...
  • Java实现滑动窗口

    千次阅读 2020-04-19 23:34:09
    滑动窗口 问题 LeetCode:239. 滑动窗口最大值 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口...
  • 1.流量控制: 1.流量控制往往指的是点对点通信量的控制,是个端到端的问题。 2.流量控制所要做的就是控制...双方确定的窗口值是400.再设每一个报文段为100字节长,序号的初始值为seq=1, 图中的箭头上面大写ACK,...
  • *以下是TCP编程中多线程服务端 */ import java.net.ServerSocket; import java.net.Socket; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Date; import java.io....
  • java通过TCP字节流传输和读取数据

    万次阅读 2013-08-08 15:48:21
    java网络编程时,基于tcp协议时,客户端和服务端都有一个输入流和输出流.但是当需要通过这个流来传输多种信息的时候,比如传文件时先传文件名,再传文件内容,但是单从流本身来看是无法知道从流中读取多少字节是文件名,...
  • Java TCP 文件传输问题

    千次阅读 2009-04-16 14:04:00
    下面是我文件传输部分使用的原理 我使用的是Tcp连接,发送方通过一个打开窗口让用户选择要发送的文件,然后将接收者的IP和文件名传递给一个专门用于文件发送的线程sendThreadHandler,在这个线程类中,通过接收到IP...
  • Java 基于Tcp/ip连接的多人交互聊天室(Socket编程)

    千次阅读 多人点赞 2016-06-06 11:45:45
    本项目由三个.java文件(Client.java、Server.java、UI.java)和一个.jpg图片文件组成UI.java是负责界面的构成文件。本聊天室的界面极其简单。主要分为两个界面:第一个是启动时需要登陆的界面如下: 输入名字进去...
  • 本文通过Java实现TCP Socket通信,并将其用于计算机端、Android手机端,同时做到代码规范化,实现代码最大化复用。 本文代码可在GitHub下载,建议对照源码阅读文章 https://github.com/jzj1993/JavaTcpSocket ...
  • 要点: *(1)ObjectInputStream和...*(2)使用ObjectInputStream和ObjectOutputStream来包装底层网络字节,TCP服务器和TCP客户端之间就可以传递对象类型的数据。 代码实现 import java.io.*; import java.n
  • 程序需要打开两个命令窗口,一个用于充当服务器,一个充当客户端   import java.io.*; import java.net.*; /* 练习:编写程序模拟TCP客户端并发上传图片,本地计算机充当服务器,并监听10010端口 客户端:...
  • JAVA实现简易的TCP通信

    2019-05-10 14:54:33
    3.连接建立成功后,在CLIENT窗口提示“连接成功!请输入密码:”。 4.在CLIENT命令行中输入密码,密码123456。 5.在SERVER接收密码并判断,若密码正确,向CLIENT返回:“Correct password!”。若密码错误,向...
  • 一、TCP的滑动窗口大小实际上就是socket的接收缓冲区大小的字节数   二、对于server端的socket一定要在listen之前设置缓冲区大小,因为,accept时新产生的socket会继承监听socket的缓冲区大小。对于client端的...
  • socket.setSoLinger(true, 0); // socket是阻塞IO,数据不发送完默认是关不掉的,需要设置soLinger socket.close();
  • 这里用java语言编写带界面的多人聊天代码,用的是TCP协议传输数据
  • 1、基于TCP的socket编程。• java.net.ServerSocket是用来创建服务器端的套接字socket。• java.net.Socket是用来创建客户端的套接字socket。• InetAddress(java.net.InetAddress)类:用来表示IP地址。• 凡事...
  • 服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于...客户端,使用Java socket通信对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指
  • Java面试之TCP与UDP

    2019-01-07 10:56:39
    1、为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样。...
  • 基于TCP协议的Java聊天小程序

    千次阅读 2017-08-28 14:50:46
    基于TCP协议的Java聊天小程序 一、基本思路 1.1 利用ServerSocket和Socket通信基本原理 Java.net包中提供了ServerSocket和Socket类来实现基于TCP的通信。利用ServerSocket可以创建服务器,利用Socket类...
  • Java TCP实现高仿版QQ聊天(一)

    千次阅读 多人点赞 2020-04-24 09:46:32
    Java TCP实现高仿版QQ聊天 前言 ​ 记录一下这套简陋的系统说明,把所遇到的问题和难点以及操作说明在这篇文档中说明清楚,当个回顾吧。万一以后那一天查看也能及时找到问题。这套系统是在本人大三时期完成的,还...
  • TCP 理论概述与 Java 编码入门

    千次阅读 2018-10-14 09:11:37
    TCPJava TCP 服务器端编码 TCP 客户端端编码 连接超时与读取超时 网络编程 TCP 简 介 1、TCP(Transmission Control Protocol) 是 socket 上的一种提供可靠的数据传输的通信协议——传输控制协议 2、TCP ...
  • Java Socket】TCP协议的多人聊天室

    千次阅读 2019-04-11 22:20:58
    最近老师叫我们做一个基于Socket的多人聊天室,网上很多教程都只讲了如何...这是基于TCP协议实现的Socket多人聊天室,分别用到ServerSocket(服务器)和Socket(客户端),说到TCP当然也离不开建立我们常常听到的...
  • 滑动窗口计数java实现

    千次阅读 2016-02-20 13:13:36
    滑动窗口计数有很多使用场景,比如说限流防止系统雪崩。相比计数实现,滑动窗口实现会更加平滑,能自动消除毛刺。   概念上可以参考TCP的滑窗算法,可以看一下这篇文章...2, 基于 java8 编译器 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,091
精华内容 18,036
关键字:

java设置tcp窗口

java 订阅