TCP显式编码报文长度发送与接收

news/2025/2/12 13:18:19/

报文格式最重要的是如何确定报文的边界。常见的报文格式有两种方法,一种是发送端把要发送的报文长度预先通过报文告知给接收端;另一种是通过一些特殊的字符来进行边界的划分。
这篇文章中讲的是发送报文长度的方法。报文类型如下:
在这里插入图片描述

第一部分是4个字节大小的消息长度,其目的是将真正发送的字节流的大小显式通过报文告知接收端,第二部分是 4 个字节大小的消息类型,第2部分才是真正需要发送的数据。

发送端

readnMessageByLength.c里边的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<errno.h>
#include<syslog.h>
#include<signal.h>
size_t readn(int fd, void *buffer, size_t length);
size_t read_message(int fd, char *buffer, size_t length);static int count;static void sig_int(int signo) {printf("\nreceived %d datagrams\n", count);exit(0);
}int main(int argc, char **argv) {if (argc != 2) {printf("usage: select01 <IPaddress> or <Port>\n");}int listenfd;listenfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port=htons(atoi(argv[1]));int on = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));int rt1 = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr));if (rt1 < 0) {printf("bind failed");exit(errno);}int rt2 = listen(listenfd, 5);if (rt2 < 0) {printf("listen failed");exit(errno);}signal(SIGPIPE, SIG_IGN);int connfd;struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);if ((connfd = accept(listenfd, (struct sockaddr *) &client_addr, &client_len)) < 0) {printf("bind failed");exit(errno);}char buf[128];count = 0;while (1) {int n = read_message(connfd, buf, sizeof(buf));if (n < 0) {printf("error read message\n");exit(errno);} else if (n == 0) {printf("client closed \n");exit(0);}buf[n] = 0;printf("received %d bytes: %s\n", n, buf);count++;}exit(0);}size_t readn(int fd, void *buffer, size_t length) {size_t count;ssize_t nread;char *ptr;ptr = buffer;count = length;while (count > 0) {nread = read(fd, ptr, count);if (nread < 0) {if (errno == EINTR)continue;elsereturn (-1);} else if (nread == 0)break;                /* EOF */count -= nread;ptr += nread;}return (length - count);        /* return >= 0 */
}size_t read_message(int fd, char *buffer, size_t length) {u_int32_t msg_length;u_int32_t msg_type;int rc;rc = readn(fd, (char *) &msg_length, sizeof(u_int32_t));if (rc != sizeof(u_int32_t))return rc < 0 ? -1 : 0;msg_length = ntohl(msg_length);rc = readn(fd, (char *) &msg_type, sizeof(msg_type));if (rc != sizeof(u_int32_t))return rc < 0 ? -1 : 0;if (msg_length > length) {return -1;}rc = readn(fd, buffer, msg_length);if (rc != msg_length)return rc < 0 ? -1 : 0;return rc;
}

gcc readnMessageByLength.c -o readnMessageByLength编译,./readnMessageByLength 8080运行。

接收端

SendMessageByLength.c里边的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<errno.h>
#include<syslog.h>
#include<signal.h>
int main(int argc, char **argv) {if (argc != 3) {printf("usage: tcpclient <IPaddress>\n");exit(errno);}int socket_fd;socket_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[2]));;inet_pton(AF_INET, argv[1], &server_addr.sin_addr);socklen_t server_len = sizeof(server_addr);int connect_rt = connect(socket_fd, (struct sockaddr *) &server_addr, server_len);if (connect_rt < 0) {fprintf(stderr, "error in connect: %s (%d)\n", strerror(errno), errno);exit(errno);}struct {u_int32_t message_length;u_int32_t message_type;char buf[128];} message;int n;while (fgets(message.buf, sizeof(message.buf), stdin) != NULL) {n = strlen(message.buf);message.message_length = htonl(n);message.message_type = 1;if (send(socket_fd, (char *) &message, sizeof(message.message_length) + sizeof(message.message_type) + n, 0) < 0){fprintf(stderr, "error in send: %s (%d)\n", strerror(errno), errno);exit(errno);}}exit(0);
}

gcc SendMessageByLength.c -o SendMessageByLength编译,./SendMessageByLength 127.0.0.1 8080运行。


http://www.ppmy.cn/news/1254658.html

相关文章

深度学习记录--logistic回归损失函数向量化实现

前言 再次明确向量化的目的&#xff1a;减少for循环的使用&#xff0c;以更少的代码量和更快的速度来实现程序 正向传播的向量化 对于,用向量化来实现&#xff0c;只需要就可以完成&#xff0c;其中,, ps.这里b只是一个常数&#xff0c;但是依然可以加在每个向量里(python的…

2000-2021年上市公司过度负债数据

2000-2021年上市公司过度负债数据 1、时间&#xff1a;2000-2021年 2、指标&#xff1a; 证券代码、证券简称、会计期间、上市日期、行业代码、行业名称、是否剔除ST或*ST股、是否剔除当年新上市、已经退市或被暂停退市的公司、产权性质、盈利能力、杠杆率行业中位数、成长性…

蓝桥杯第1037题子串分值和 C++ 字符串 逆向思维 巧解

题目 思路和解题方法 方案一——遍历哈希表 仅能过60%样例,大多数同学都用的该方法&#xff0c;就不过多赘述 #include <iostream> #include <unordered_map> using namespace std; int main() {string s;cin >> s;int n s.size();int res n;for (int i 0…

前端页面转pdf

首先&#xff0c;需要安装两个库 html2canvasjspdf 先引入这个公用的html转pdf的方法 /**path:src/utils/htmlToPdf.jsname:导出页面为pdf格式 **/ import html2Canvas from "html2canvas1.4.1"; import JsPDF from "jspdf2.5.1";const htmlToPdf {get…

sCrypt 现已支持各类主流前端框架

sCrypt 现已支持各类主流前端框架&#xff0c;包括&#xff1a; ReactNext.jsAngularSvelteVue 3.x or 2.x bundled with Vite or Webpack 通过在这些支持的前端框架中集成sCrypt开发环境&#xff0c;你可以直接在前端项目里访问合约实例和调用合约&#xff0c;方便用户使用Se…

Nacos2.x配置中心源码分析

概述 源码注释参考 git 仓库&#xff0c;对应流程图后续补充&#xff1b; 启动 nacos nacos 启动类&#xff1a; // com.alibaba.nacos.NacosSpringBootApplication(scanBasePackages "com.alibaba.nacos") ServletComponentScan EnableScheduling public class…

【JavaSE】API(学习笔记)

一、Math 包含执行基本数字运算的方法没有构造方法&#xff0c;但方法是静态的&#xff0c;可以用类名直接调用 1、Math类常用方法 1&#xff09;绝对值&#xff1a;abs public static int abs(int a)2&#xff09;小数的最近整数&#xff1a;ceil(最小整数) / floor(最大整…

drawio画图工具的四种使用方式

1、免安装使用&#xff08;绿色版&#xff09; 这种直接下载下来直接就可以使用&#xff0c;属于绿色版&#xff08;开箱即用&#xff09;&#xff0c;适用于个人 点击下载地址 2、 安装使用 这种下载下来就需要安装才可使用&#xff0c;适用于个人 点击下载地址 3、war包…