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

报文格式最重要的是如何确定报文的边界。常见的报文格式有两种方法,一种是发送端把要发送的报文长度预先通过报文告知给接收端;另一种是通过一些特殊的字符来进行边界的划分。

这篇文章中讲的是发送报文长度的方法。报文类型如下:

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

发送端

readnMessageByLength.c里边的代码如下:

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;
            else
                return (-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里边的代码如下:

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运行。

相关推荐
米饭是菜qy1 小时前
TCP 三次握手意义及为什么是三次握手
服务器·网络·tcp/ip
yaoxin5211231 小时前
第十九章 TCP 客户端 服务器通信 - 数据包模式
服务器·网络·tcp/ip
IPdodo全球网络3 小时前
解析“ChatGPT网络错误”:从网络专线到IP地址的根源与解决方案
网络·tcp/ip·chatgpt
hgdlip17 小时前
本地连接IP地址的自主设置指南‌
网络·网络协议·tcp/ip·本地连接
დ旧言~19 小时前
【网络】子网掩码
服务器·网络·网络协议·tcp/ip·php·apache
前端李易安21 小时前
什么是UDP和TCP?有什么区别?应用场景分别都有哪些?
网络·tcp/ip·udp
极客小张1 天前
基于STM32的智能宠物自动喂食器设计思路:TCP\HTTP、Node.js技术
stm32·单片机·物联网·tcp/ip·node.js·毕业设计·宠物
yaoxin5211231 天前
第十八章 TCP 客户端 服务器通信 - 使用OPEN命令 - Supported参数
服务器·网络·tcp/ip
涔溪1 天前
HTTP TCP三次握手深入解析
网络·tcp/ip·http
憨子周1 天前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip