TCP 流程

TCP 处理流程图

 

 

 

TCP流程

 1、检查版本号——  

// Init WinSock

     WSADATA   data;

     int ret = WSAStartup(0x0202, &data);

     if (ret != 0)

     {

         WSACleanup();

         return FALSE;

     }

2、建立套接字,绑定,监听——

     mListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     if (mListener != INVALID_SOCKET)

     {

         // Settings on socket

         BOOL  sopt = TRUE;

         setsockopt(mListener, IPPROTO_TCP, TCP_NODELAY,

              (char *)&sopt, sizeof(BOOL));

         setsockopt(mListener, SOL_SOCKET, SO_DONTLINGER,

              (char *)&sopt, sizeof(BOOL));

 

         // Bind socket and listen

         SOCKADDR_IN  addr;

         memset(&addr, 0, sizeof(SOCKADDR_IN));

         addr.sin_family      = AF_INET;

         addr.sin_addr.s_addr = htonl(INADDR_ANY);

          addr.sin_port        = htons(mListenPort);

         val = bind(mListener, (struct sockaddr*) &addr, sizeof(addr));

         pass = (val != SOCKET_ERROR);

     }

 

     if (pass)

     {

         // Put the socket into the listening status

         val = listen(mListener, SOMAXCONN);

         pass = (val != SOCKET_ERROR);

     }

 

SOCKET socket( IN int af, IN int type, IN int protocol );

af (AF_INET) ARPA因特网协议(UNIX网络套接字);

type——SOCK_STREAM 流式套接字TCPSOCK_DGRAM 数据报套接字UCP

protocol——0为默认值,IPPROTO_TCP IP网的TCP协议

 

int setsockopt(IN SOCKET s, IN int level, IN int optname, IN const char FAR * optval, IN int optlen);

——设置套接口的选项

// TCP_NODELAY选项禁止Nagle算法

//SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。相当于将SO_LINGERl_onoff 元素置为零

 

int bind(IN SOCKET s,IN const struct sockaddr FAR * name,IN int namelen);

——绑定socket和地址和端口监听因特网上的任意ip地址接入 INADDR_ANY

 

int listen( IN SOCKET s,IN int backlog);

——backlog队列中可以容纳的未处理连接的最大数目限制

——listen函数为服务器程序创建一个队列来保存未处理的请求

 

3、接收连接

         DWORD threadID = 0;

         mLsnThread = CreateThread(NULL, 0, ListeningThrd,

              this, 0, &threadID);

 

//______________________ListenThrd 函数_______________________________

         SOCKET accepted = accept(mListener, (SOCKADDR *)&saddr, &len);

         if (accepted == INVALID_SOCKET)

         {

              return FALSE;

         }

        

         DeleteAccepted();

         mAccepted = accepted;

         // Settings on socket

         BOOL sopt = TRUE;

         setsockopt(mAccepted, IPPROTO_TCP, TCP_NODELAY,

              (char *)&sopt, sizeof(BOOL));

         setsockopt(mAccepted, SOL_SOCKET, SO_DONTLINGER,

              (char *)&sopt, sizeof(BOOL));

 

SOCKET accept(IN SOCKET s,OUT struct sockaddr FAR * addr,IN OUT int FAR * addrlen);

——accept系统调用只有当客户程序试图连接到由socket参数指定的套接字时才返回,即是说,accept将阻塞知道有客户建立连接为止。通常的做法是开一条新的线程。

——accept 返回新的套接字accepted

 

4、客户端请求连接

     mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     if (mSocket != INVALID_SOCKET)

     {

         BOOL sopt = TRUE;

         setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY,

              (char *)&sopt, sizeof(BOOL));

         setsockopt(mSocket, SOL_SOCKET, SO_DONTLINGER,

              (char *)&sopt, sizeof(BOOL));

 

         SOCKADDR_IN   saddr;

         memset(&saddr, 0, sizeof(SOCKADDR_IN));

         saddr.sin_addr.S_un.S_addr = inet_addr(inTarget);

         saddr.sin_family           = AF_INET;

         saddr.sin_port             = htons((WORD)inPort);

         if (connect(mSocket, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) != 0){

}

}

 

int connect(IN SOCKET s,IN const struct sockaddr FAR * name,IN int namelen);

——客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。

——inTarget为服务器端ip地址。

——如果连接不能立刻建立,connect调用将阻塞一段不确定的超时时间。connect调用是阻塞的。

 

5、发送/接收数据

//__recv___

DWORD threadID = 0;

         mRcvThread = CreateThread(NULL, 0, ReceivingThrd,

              this, 0, &threadID);

//_________________________ReceivingThrd 函数____________________________________

     while (mIsReceiving)

     {

         bytes = recv(mSocket, buf, sizeof(Net_Header), 0);

         if (bytes == SOCKET_ERROR || bytes == 0)

         {

              Detach();

              mIsReceiving = FALSE;

              break;

         }

 

         pHeader->my_ntoh();

         bytes = recv(mSocket, buf, pHeader->pack_size, 0);

         if (bytes == SOCKET_ERROR || bytes == 0)

         {

              Detach();

              mIsReceiving = FALSE;

              break;

         }       

         buf[bytes]  = '/0';

     }

 

//__send___

         DWORD threadID = 0;

         mSndThread = CreateThread(NULL, 0, SendingThrd,

              this, 0, &threadID);

//_________________________ SendingThrd函数____________________________________

     char   str[] = "hello world!";

     int    len   = strlen(str) + sizeof(Net_Header);   

    

     Net_Header * pHeader = (Net_Header *) buf;

     pHeader->pack_size   = strlen(str);

     pHeader->my_hton();

     strcpy(buf+sizeof(Net_Header), str);

 

     bytes = send(mSocket, buf, len, 0);

     }

 

 

int recv( IN SOCKET s, OUT char FAR * buf, IN int len, IN int flags );

int send( IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags );

——开新线程来发送和接收数据

 

6、关闭socket连接

     if (mSocket != INVALID_SOCKET)

     {

         closesocket(mSocket);

         mSocket = INVALID_SOCKET;

         mIsConnected = FALSE;

     }

 

7、关闭发送接收数据线程

     if (mIsReceiving)

     {

         Detach();

         if (mRcvThread != NULL)

         {

              WaitForSingleObject(mRcvThread, INFINITE);

              mRcvThread = NULL;

         }

     }

//__________

     if (mIsReceiving)

     {

         Detach();

         if (mRcvThread != NULL)

         {

              WaitForSingleObject(mRcvThread, INFINITE);

              mRcvThread = NULL;

         }

     }

 

——listen同样需要关闭socket和监听线程

<think>首先,用户的问题是关于Modbus TCP通信的工作流程:"我想了解Modbus TCP通信的工作流程 请问Modbus TCP协议通信流程详解"。我需要根据系统级指令来回答。 系统级指令要求: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层要求: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用: - 引用[1]: Modbus TCP通信协议介绍及示例详解。它提到Modbus TCP是Modbus协议在以太网上的实现,保留了核心功能和数据模型,调整了消息封装。 - 引用[2]: Modbus TCP协议介绍(ModbusTCP),包括协议结构、帧格式、通信过程等。 用户的问题是关于工作流程的,所以我需要基于这些引用构建一个清晰的解释。 结构化回答: 1. **介绍Modbus TCP**:简要解释什么是Modbus TCP,基于引用。 2. **工作流程详解**:逐步描述通信流程,包括建立连接、请求响应等。 3. **关键组件**:解释帧格式、数据模型等。 4. **示例**:提供请求和响应帧的示例。 5. **生成相关问题**:在回答结束时,添加相关问题。 确保使用中文。 工作流程基于引用[2]: - 建立连接:客户端通过TCP连接到服务器的502端口。 - 请求与响应:客户端发送请求帧,服务器处理并发送响应帧。 - 帧格式:包括MBAP头部和PDU部分。 MBAP头部: - Transaction Identifier (事务标识符) - Protocol Identifier (协议标识符) - Length (长度) - Unit Identifier (单元标识符) PDU部分:包括功能码和数据。 数据模型: - 离散输入 - 线圈 - 输入寄存器 - 保持寄存器 回答结构: - 开头:介绍Modbus TCP。 - 主体:详细工作流程。 - 结尾:总结并添加相关问题。 在引用部分,自然地添加引用标识。例如,描述时在末尾加[^1]或[^2]。 生成相关问题: - 类似示例中的问题,如性能分析、优化方法、应用场景。 相关问题想法: 1. Modbus TCP如何确保数据传输的可靠性? 2. Modbus TCP与其他协议如Modbus RTU的区别是什么? 3. 在工业自动化中,Modbus TCP的常见问题及解决方案? 最终回答格式: - 使用标题或小节使结构清晰。 - 避免使用Markdown,但可以分段。 - 在结束时添加“相关问题”部分。 LaTeX:由于主题是通信协议,可能没有太多数学表达式。如果有变量或方程,确保使用$...$。例如,提到长度或标识符时,可以用$表示变量。 完整回答: 1. **Modbus TCP简介** 2. **通信工作流程** - 建立连接 - 请求帧结构 - 响应帧结构 - 通信过程 3. **数据模型** 4. **示例** 5. **总结** 6. **相关问题**</think>### Modbus TCP通信工作流程详解 Modbus TCP是一种基于以太网的工业通信协议,它在TCP/IP协议栈上实现Modbus的核心功能,保留了简单的请求-响应模型和数据模型(如离散输入、线圈、输入寄存器和保持寄存器)[^1]。通信工作流程主要包括建立连接、请求发送、服务器处理和响应返回四个步骤。下面我将逐步解释,确保内容真实可靠,基于标准协议规范。 #### 1. **建立连接** - Modbus TCP客户端(如PLC或HMI)通过TCP协议主动连接到服务器的502端口(默认端口)。服务器在502端口监听连接请求。 - 连接建立后,客户端和服务器之间形成一个持久的TCP会话。TCP协议负责确保数据传输的可靠性,如通过三次握手建立连接、错误检测和重传机制[^2]。 - 关键点:连接是面向连接的,不同于无连接的UDP协议,这保证了数据完整性。例如,事务标识符(Transaction Identifier)用于匹配请求和响应,防止数据包丢失或乱序。 #### 2. **请求帧发送** 客户端构建一个Modbus TCP请求帧并发送给服务器。帧格式包括MBAP头部(Modbus Application Protocol Header)和PDU(Protocol Data Unit)部分: - **MBAP头部(7字节)**: - 事务标识符(Transaction Identifier):2字节,用于唯一标识一个事务(例如$0x0001$)。 - 协议标识符(Protocol Identifier):2字节,固定为$0x0000$(表示Modbus协议)。 - 长度(Length):2字节,表示后续PDU部分的字节数。 - 单元标识符(Unit Identifier):1字节,用于标识服务器上的设备(如$0x01$表示第一个从设备)。 - **PDU部分**:包含功能码和数据: - 功能码(Function Code):1字节,指定操作类型(如$0x03$表示读取保持寄存器)。 - 数据(Data):可变长度,包含具体参数(如寄存器地址和数量)。 - 示例请求帧(读取保持寄存器): ``` Transaction ID: 0x0001, Protocol ID: 0x0000, Length: 0x0006, Unit ID: 0x01 PDU: Function Code 0x03, Starting Address 0x0000, Quantity 0x0002 ``` 这表示客户端请求读取从地址$0x0000$开始的2个保持寄存器[^2]。 #### 3. **服务器处理** - 服务器收到请求帧后,解析MBAP头部和PDU: - 验证事务标识符和单元标识符,确保请求合法。 - 根据功能码执行操作(如读取寄存器、写入线圈)。例如,功能码$0x03$对应读取保持寄存器,服务器会从内部数据存储中获取指定寄存器的值。 - 如果请求无效(如功能码不支持),服务器返回异常响应(功能码+$0x80$,并添加错误码)。 - 处理过程基于Modbus数据模型: - 离散输入(Discrete Inputs):只读布尔值(如传感器状态)。 - 线圈(Coils):可读写布尔值(如继电器控制)。 - 输入寄存器(Input Registers):只读16位值(如温度读数)。 - 保持寄存器(Holding Registers):可读写16位值(如设定参数)[^2]。 #### 4. **响应帧返回** 服务器构建响应帧并发送回客户端: - **MBAP头部**:与请求帧相同的事务标识符,确保客户端能匹配响应。 - **PDU部分**: - 功能码:与请求一致(成功时)或功能码+$0x80$(异常时)。 - 数据:包含操作结果(如寄存器值)。 - 示例响应帧(成功读取): ``` Transaction ID: 0x0001, Protocol ID: 0x0000, Length: 0x05, Unit ID: 0x01 PDU: Function Code 0x03, Byte Count 0x04, Register Values 0x1234, 0x5678 ``` 这表示服务器返回了两个寄存器的值:$0x1234$和$0x5678$[^2]。 - 通信结束后,TCP连接可保持打开以复用(减少开销),或由客户端关闭。 #### 工作流程总结 整个流程是同步的:客户端发送请求→服务器处理→服务器返回响应。典型的事务耗时在毫秒级,适用于工业自动化场景(如SCADA系统)。Modbus TCP的优势在于简单、高效,但依赖TCP的可靠性机制处理网络问题[^1][^2]。 ### 相关问题 1. Modbus TCP如何处理网络延迟或数据包丢失? 2. Modbus TCP与Modbus RTU协议在通信流程上有哪些关键区别? 3. 在工业物联网(IIoT)中,Modbus TCP的常见安全风险及防护措施是什么?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值