当前位置: 首页 > article >正文

嵌入式Linux之基于TCP协议的程序

一、服务端(single_conn_server.c)

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd, result) \
    if (result < 0)               \
    {                             \
        perror(cmd);              \
        return -1;                \
    }
void *read_from_client(void *argv)
{
    int client_fd = *(int *)argv;
    char *read_buf = NULL;
    ssize_t count = 0;
    read_buf = malloc(sizeof(char) * 1024);
    if (!read_buf)
    {
        perror("malloc server read_buf");
        return NULL;
    }
    while ((count = recv(client_fd, read_buf, 1024, 0)))
    {
        if (count < 0)
        {
            perror("recv");
        }
        fputs(read_buf, stdout);
    }
    printf("客户端请求关闭连接......\n");
    free(read_buf);
    return NULL;
}
void *write_to_client(void *argv)
{
    int client_fd = *(int *)argv;
    char *write_buf = NULL;
    ssize_t send_count = 0;
    write_buf = malloc(sizeof(char) * 1024);
    if (!write_buf)
    {
        printf("写缓存分配失败,断开连接\n");
        shutdown(client_fd, SHUT_WR);
        perror("malloc server write_buf");
        return NULL;
    }
    while (fgets(write_buf, 1024, stdin) != NULL)
    {
        send_count = send(client_fd, write_buf, 1024, 0);
        if (send_count < 0)
        {
            perror("send");
        }
    }
    printf("接收到命令行的终止信号,不再写入,关闭连接......\n");
    shutdown(client_fd, SHUT_WR);
    free(write_buf);
    return NULL;
}
int main(int argc, char const *argv[])
{
    int sockfd, temp_result, client_fd;
    pthread_t pid_read, pid_write;
    struct sockaddr_in server_addr, client_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));
    // 声明 IPV4 通信协议
    server_addr.sin_family = AF_INET;
    // 我们需要绑定 0.0.0.0 地址,转换成网络字节序后完成设置
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    // 端口随便用一个,但是不要用特权端口
    server_addr.sin_port = htons(6666);
    // 创建 server socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    handle_error("socket", sockfd);
    // 绑定地址
    temp_result = bind(sockfd, (struct sockaddr *)&server_addr,
                       sizeof(server_addr));
    handle_error("bind", temp_result);
    // 进入监听模式
    temp_result = listen(sockfd, 128);
    handle_error("listen", temp_result);
    // 接受第一个 client 连接
    socklen_t cliaddr_len = sizeof(client_addr);
    client_fd = accept(sockfd, (struct sockaddr *)&client_addr,
                       &cliaddr_len);
    handle_error("accept", client_fd);
    printf("与客户端 from %s at PORT %d 文件描述符 %d 建立连接\n",
           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),
           client_fd);
    // 启动一个子线程,用来读取客户端数据,并打印到 stdout
    pthread_create(&pid_read, NULL, read_from_client, (void *)&client_fd);
    // 启动一个子线程,用来从命令行读取数据并发送到客户端
    pthread_create(&pid_write, NULL, write_to_client, (void *)&client_fd);
    // 阻塞主线程
    pthread_join(pid_read, NULL);
    pthread_join(pid_write, NULL);
    printf("释放资源\n");
    close(client_fd);
    close(sockfd);
    return 0;
}

二、客户端(single_conn_client.c)

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
// 192.168.10.150 IP 地址的 16 进制表示
#define INADDR_LOCAL 0xC0A80A96
#define handle_error(cmd, result) \
    if (result < 0)               \
    {                             \
        perror(cmd);              \
        return -1;                \
    }
void *read_from_server(void *argv)
{
    int sockfd = *(int *)argv;
    char *read_buf = NULL;
    ssize_t count = 0;
    read_buf = malloc(sizeof(char) * 1024);
    if (!read_buf)
    {
        perror("malloc client read_buf");
        return NULL;
    }
    while (count = recv(sockfd, read_buf, 1024, 0))
    {
        if (count < 0)
        {
            perror("recv");
        }
        fputs(read_buf, stdout);
    }
    printf("收到服务端的终止信号......\n");
    free(read_buf);
    return NULL;
}
void *write_to_server(void *argv)
{
    int sockfd = *(int *)argv;
    char *write_buf = NULL;
    ssize_t send_count = 0;
    write_buf = malloc(sizeof(char) * 1024);
    if (!write_buf)
    {
        printf("写缓存分配失败,断开连接\n");
        shutdown(sockfd, SHUT_WR);
        perror("malloc client write_buf");
        return NULL;
    }
    while (fgets(write_buf, 1024, stdin) != NULL)
    {
        send_count = send(sockfd, write_buf, 1024, 0);
        if (send_count < 0)
        {
            perror("send");
        }
    }
    printf("接收到命令行的终止信号,不再写入,关闭连接......\n");
    shutdown(sockfd, SHUT_WR);
    free(write_buf);
    return NULL;
}
int main(int argc, char const *argv[])
{
    int sockfd, temp_result;
    pthread_t pid_read, pid_write;
    struct sockaddr_in server_addr, client_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));
    server_addr.sin_family = AF_INET;
    // 连接本机 127.0.0.1
    server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    // 连接端口 6666
    server_addr.sin_port = htons(6666);
    client_addr.sin_family = AF_INET;
    // 连接本机 192.168.10.150
    client_addr.sin_addr.s_addr = htonl(INADDR_LOCAL);
    // 连接端口 8888
    client_addr.sin_port = htons(8888);
    // 创建 socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    handle_error("socket", sockfd);
    temp_result = bind(sockfd, (struct sockaddr *)&client_addr,
                       sizeof(client_addr));
    handle_error("bind", temp_result);
    // 连接 server
    temp_result = connect(sockfd, (struct sockaddr *)&server_addr,
                          sizeof(server_addr));
    handle_error("connect", temp_result);
    // 启动一个子线程,用来读取服务端数据,并打印到 stdout
    pthread_create(&pid_read, NULL, read_from_server, (void *)&sockfd);
    // 启动一个子线程,用来从命令行读取数据并发送到服务端
    pthread_create(&pid_write, NULL, write_to_server, (void *)&sockfd);
    // 阻塞主线程
    pthread_join(pid_read, NULL);
    pthread_join(pid_write, NULL);
    printf("关闭资源\n");
    close(sockfd);
    return 0;
}

三、解析

        在上述例程中,我们将客户端绑定到了 192.168.10.150 的 8888 端口, 192.168.10.150 实际上是本机 IP,此处等价于 localhost 或 127.0.0.1。此外,通常服务端不需要绑定到具体的 IP 和端口,如果不绑定,启动后会操作系统会随机为客户端分配本机的某个端口。我们这里将客户端绑定至指定的 IP 和端口,主要是为了在分析时便于区分客户端和服务端,实际的客户端程序完全可以省去这一步。
在 Makefile 开头补充伪目标定义和变量定义

.PHONY: single_conn single_conn_clean

single_conn_executables:=single_conn_server single_conn_client

Makefile 末尾补充目标声明

single_conn_server: single_conn_server.c 
    -$(CC) -o $@ $^

single_conn_client: single_conn_client.c
    -$(CC) -o $@ $^

single_conn: $(single_conn_executables)

single_conn_clean:
    -rm ./$(word 1, $(single_conn_executables)) ./$(word 2,
$(single_conn_executables))

通过 Makefile 编译后,打开两个命令窗口,执行两个端,即可实现数据互发!。


http://www.kler.cn/a/503497.html

相关文章:

  • 【微信小程序】5|我的页面 | 我的咖啡店-综合实训
  • 计算机视觉算法实战——步态识别(主页有源码)
  • vue3+ts+element-plus 对话框el-dialog设置圆角
  • Three.js 用户交互:构建沉浸式3D体验的关键
  • WINFORM - DevExpress -> devexpress版--报表(report)
  • 信息安全、网络安全和数据安全的区别和联系
  • 配置Allure环境变量【macOS版】
  • 麒麟系统设置tomcat开机自启动
  • Unity-Mirror网络框架-从入门到精通之RigidbodyPhysics示例
  • 【Ubuntu与Linux操作系统:二、图形界面与命令行】
  • 【机器人】SceneGrasp 同时支持3D物体重建、6D位姿估计、抓取点估计
  • 字符串算法篇——字里乾坤,算法织梦,解构字符串的艺术(下)
  • JAVA实战开源项目:课程管理平台(Vue+SpringBoot) 附源码
  • 主析取范式
  • 【HarmonyOS NEXT】鸿蒙跳转华为应用市场目标APP下载页
  • 视频转码对画质有影响吗?视频融合平台EasyCVR支持哪些转码格式?
  • Linux SUID提权
  • Prompt工程框架介绍与场景选择
  • Mysql--架构篇--体系结构(连接层,SQL层,存储引擎层,文件存储层)
  • 《o3模型的突破:AI从模仿到推理的关键转折》
  • 相机小孔成像模型与透视变换
  • Vue 学习之旅:核心技术学习总结与实战案例分享(vue指令下+计算属性+侦听器)
  • 一键掌握多平台短视频矩阵营销/源码部署
  • ChordCraft荣获重要认可:推动全球音乐教育的数字化革新
  • stack和queue专题
  • 使用 versions-maven-plugin 和 flatten-maven-plugin 插件惯例 maven 项目版本