首页 > 系统相关 >嵌入式Linux之基于TCP协议的程序

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

时间:2025-01-19 16:04:21浏览次数:3  
标签:addr read TCP server write client Linux 嵌入式 buf

一、服务端(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 编译后,打开两个命令窗口,执行两个端,即可实现数据互发!。

标签:addr,read,TCP,server,write,client,Linux,嵌入式,buf
From: https://blog.csdn.net/qq_68192341/article/details/145115259

相关文章

  • 学技术学英语:TCP的三次握手和四次挥手
    单词汉语意思音标acknowledge承认,确认/əkˈnɒl.ɪdʒ/acknowledgment确认,承认/əkˈnɒl.ɪdʒ.mənt/duplex双向的/ˈdjuː.pleks/establish建立/ɪˈstæb.lɪʃ/handshake握手,握手协议/ˈhænd.ʃeɪk/re-transmission重传/ˌri......
  • [2025.1.19 JavaSE学习]网络编程-2(netstat指令 && TCP补充)
    netstatnetstat-an:可以查看当前主机网络情况,包括端口监听情况和网络连接情况netstat-an|more:可以分页显示在dos控制台执行Listening表示某个端口在监听如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息PS:netstat-anb,可以发现,8888端口号在上一节程序运行......
  • ingress-nginx代理tcp使其能外部访问mysql
    一、helm部署mysql主从复制helmrepoaddbitnamihttps://charts.bitnami.com/bitnamihelmrepoupdate helmpullbitnami/mysql 解压后编辑values.yaml文件,修改如下(storageclass已设置默认类)117##@paramarchitectureMySQLarchitecture(`standalone`or`re......
  • Linux 上安装 Node.js
    在Linux上安装Node.js的方法取决于你使用的发行版。以下是常见的几种安装方法:方法1:通过包管理器安装(推荐)对于Ubuntu/Debian系统:更新系统包索引:sudoaptupdate安装Node.js(LTS版本)你可以直接使用Ubuntu/Debian的官方包管理器安装Node.js,但是推荐使用NodeS......
  • 【自学嵌入式(4)STA模式、AP模式、体验天气时钟】
    STA模式、AP模式、体验天气时钟一、无线终端模式*概念**特点**优点**缺点*二、接入点模式*概念**特点**优点**缺点*三、天气时钟的实现上一篇文章主要展示的是无线终端模式的应用(两个开发板都作为终端设备,连接外部网络);为了了解开发板作为无线接入点(AP)的功能,前几天......
  • 【自学嵌入式(5)环形电阻、数码管、随机数字程序】
    环形电阻、数码管、随机数字程序环形电阻*概念**特点*共阴极数码管*概念*随机数字程序本篇文章将会手动搭建一个电路,使用到的硬件主要有面包板一个、若干杜邦线、UNO开发板一个、按键开关一个、一个200-500欧姆的电阻、共阴极数码管一个由于对即将要使用的环形电......
  • Linux基础-指令篇03【入门级】
    Linux基础-文件操作内容概要本文主要介绍了在linux系统中如何通过终端指令对文件以及文件内容进行增删改查。同时上传了关于存储转换的小知识点。指令cat/less/more/head/tailcat:查看文件内容(少)执行权限:所有用户语法:cat[选项]文件选项-n:显示文件行号范例......
  • Linux 打印服务RCE漏洞:HackTheBox 【Evilcups】 复现
    靶场概述:2024年9月26日,一位名为SimoneMargaritelli的研究人员发布了有关CUPS漏洞的研究。其中包括四个CVE:CVE-2024-47176-通常侦听所有UDP631接口的服务,允许远程将打印机添加到机器。此漏洞允许任何能够访问此机器的攻击者触发“获取打印机属性”互联网打......
  • Linux的几个基本指令
    文章目录一、几个基本指令1、ls指令注意!2、pwd命令3、touch指令4、mkdir指令注意!注意!5、cd指令注意!6、cp指令今天我们学习Linux下的几个基本指令,本篇是在Xshell环境下执行的。一、几个基本指令1、ls指令功能:对于目录,该命令目的是列出该目录下的所有子......
  • Linux中常用命令详解
        在Linux中,有很多常用命令可以帮助你完成日常操作。以下是一些常用Linux命令的详细介绍:1.ls-列出目录内容语法:ls[选项][目录]常用选项:-l:显示详细信息(权限、文件大小、修改时间等)-a:显示所有文件,包括隐藏文件(以.开头)-h:以可读的方式显示文件......