Multi-Thread TCP Server & Client

prerequisite knowledge:
Basic TCP Server & Client: URL

Server

#include <stdio.h>
#include <string.h>
#include <unistd.h> // read and write (TCP); sendto and recvfrom (UDP)
#include <arpa/inet.h> // 包含#include <sys/socket.h>
#include <pthread.h>

// 想要将两份参数通过一个位置传递给working 得定义一个信息结构体
struct SockInfo {
    struct sockaddr_in addr;
    int fd;
};

// 由于有多个客户端 所以有多份信息 搞个数组存一下
struct SockInfo infos[512]; // 意味着我们最多只能和512个客户端*同时*通信
// 注意线程池是多个工作线程组成的队列,与此处不同

/* infos需要上锁吗?
   不需要,每个子线程都使用了数组的一个位置,即多个线程没有操控同一内存空间*/

void* working(void* arg) {
    struct SockInfo* pinfo = (struct SockInfo*)arg;
    printf("client socket %d, Address: %s:%d\n", pinfo->fd, inet_ntoa(pinfo->addr.sin_addr), ntohs(pinfo->addr.sin_port));
    // 5. 通信
    while (1) {
        char buf[1024];
        int len = recv(pinfo->fd, buf, sizeof(buf), 0);
        if (len > 0) {
            printf("client say: %s\n", buf);
            send(pinfo->fd, buf, len, 0); // 长度指定为len 不要传多了
        } else if (len == 0) {
            printf("客户端已经断开连接...\n");
            break;
        } else if (len == -1) {
            perror("recv");
            break;
        }
    } // 跳出后说明通信结束
    pinfo->fd = -1; // 回收
    close(pinfo->fd);
    return NULL;
}

int main(int argc, char* argv[]) {
    // 1. 创建监听fd
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        return -1;
    }
    // 2. 绑定监听fd
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr)); 
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY; // 宏INADDR_ANY(可以绑定本地)实际值是0=0.0.0.0;由于大小端没区别,因此无需htonl
    int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (ret == -1) {
        perror("bind");
        return -1;
    }
    // 3. 设置监听
    ret = listen(fd, SOMAXCONN); // #define SOMAXCONN 128 // 最大监听队列长度 内部定义过来
    if (ret == -1) {
        perror("listen");
        return -1;
    }

    // 初始化信息数组
    int max = sizeof(infos) / sizeof(infos[0]);
    for (int i=0; i<max; ++i) {
        memset(&infos[i], 0, sizeof(infos[i]));
        infos[i].fd = -1; // -1表示该数组元素未被占用
    }

    // 4. 阻塞并等待客户端连接
    // struct sockaddr_in caddr; // 这个就不需要了
    // memset(&caddr, 0, sizeof(caddr));
    socklen_t caddr_len = sizeof(struct sockaddr_in);

    // 主线程不断接收:
    while (1) {
        // 创建子线程
        pthread_t tid;
        struct SockInfo* pinfo;
        for (int i=0; i<max; ++i) {
            if (infos[i].fd == -1) {
                pinfo = &infos[i];
                break;
            }
        }
        int cfd = accept(fd, (struct sockaddr*)&pinfo->addr, &caddr_len); // 直接传入信息数组中空闲元素的addr
        pinfo->fd = cfd; // 传递cfd
        if (cfd == -1) {
            perror("accept");
            break;
        }
        pthread_create(&tid, NULL, working, pinfo);
        // 此处不能使用主线程回收子线程资源 因为pthread_join是阻塞函数 与我们设想的不断accept矛盾
        pthread_detach(tid);
    }

    close(fd);
    return 0;
}

Client

#include <stdio.h>
#include <string.h>
#include <unistd.h> // read and write (TCP); sendto and recvfrom (UDP)
#include <arpa/inet.h> // 包含#include <sys/socket.h>

int main(int argc, char* argv[]) {
    // 1. 创建通信fd
    int fd = socket(PF_INET, SOCK_STREAM, 0); // AF_*和PF_*值完全相同,通常混用
    if (fd == -1) {
        perror("socket");
        return -1;
    }
    // 2. 连接服务器
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr)); 
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 或者直接指定ip: 172.31.78.11
    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (ret == -1) {
        perror("connect");
        return -1;
    }
    printf("socket connect successful!\n");
    // 3. 通信
    int number = 0;
    while (1) {
        char buf[1024];
        sprintf(buf, "hello, message number #%d...\n", number++); // sprintf将数据写入字符串 而非输出到标准输出流
        send(fd, buf, strlen(buf)+1, 0); // 注意这里不要发送sizeof(buf),发送实际字符数+'\0'
        memset(buf, 0, sizeof(buf)); // 有必要清空buf的
        int len = recv(fd, buf, sizeof(buf), 0);
        if (len > 0) {
            printf("server say: %s\n", buf);
        } else if (len == 0) {
            printf("服务器已经断开连接...\n");
            break;
        } else if (len == -1) {
            perror("recv");
            break;
        }
        sleep(1); // 让客户端1秒发一条
    } // 跳出后说明通信结束
    close(fd);
    return 0;
}

makefile

server:
	gcc -o server server.c -lpthread &
	gcc -o client client.c

clean:
	rm -f server client
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为MULTI-TOOL项目的延续 应用基于华为和荣耀智能手机的fastboot和adb模式。允许您使用Kirin和Qualcomm处理器上的任何设备执行各种操作。如果您不熟悉华为设备,该实用程序将帮助您打开引导加载程序,安装TWRP,获取root权限等,而无需命令行知识。对于高级,它是一个经济实惠且功能强大的实用程序,而不是命令行。实用功能每天都在增长。 简要介绍项目的起源Multi-Tool:最初,llibell(inside!Out)单独工作,这一切都始于从VirusPlus翻译和改编实用程序(荣誉6 / 6plus Multi-Tool in Honor 7 Multi-tool,它是在2015年底。然后它没有计划将实用程序上传到XDA,实用程序出现了当VirusPlus建议结合努力并将两个实用程序合二为一时,它的布局是一个弯曲的翻译。这就是Honor Multi-Tool的用法。通过翻译,来自XDA的人帮助了。因此支持2个版本,俄语和英语。论坛w3bsit3-dns.com,他们开始要求为他们的模特制作一个实用工具。一个人没有应用程序 我的手是很难适应一个不同的模式。我们需要有几个人愿意提供帮助。更重要的是对自己的工具完成的工作,开始开发和改造,当模型仍然是新是非常重要的。 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 HUAWEI&HONOR的多功能工具 该实用程序的功能分为BASIC和ADVANCED。Basic具有所有支持的设备。高级,只有那些获得全力支持的人。扩展标记为橙色。 机会 自动更新到当前版本(更新自身)。 更改实用程序语言(Rus / Eng / CN)。 安装自定义恢复/ eRecovery / Recovery_Ramdisk(TWRP)。 安装自定义启动/ Ramdisk。 自定义SuperSu安装助手(Root) 重启到恢复模式| eRecovery | 快速启动和救援模式 来自固件Boot,cust,recovery,system,userdata的固件映像。 解锁/锁定引导加载程序(在本地和Team MT云中保留代码) Raskirpichivanie(Unbrick) 安装华为设备的所有驱动程序 安装华为HiSuite 安装华为Update Extractor 直流解锁安装 安装HCU 在没有Root的情况下启用隐藏/禁用的EMUI功能(EMUI Tweaker) 从在线数据库安装TWRP 从在线数据库安装SuperSu / Magisk 从在线数据库安装Stock Recovery / eRecovery / Recovery_Ramdisk和boot.img 从在线数据库安装排水Boot / Ramdisk映像 安装任何固件(RUFI)。 变化 在该计划中 开发人员 MT队 操作系统 Windows 7 sp1 /8.x/10x(无Windows XP) MAC OS - 通过Parallels。 要求 在提示.NET 4.6.1时安装 接口 俄语,英语 支持 Group to Telegram - 回答您的问题,写下错误并发送您的建议。 论坛 W3bsit3-dns.com,XDA开发人员,Smartsworld 下载 下载多工具 支持的模型 扩展支持 - 库存图片,TWRP,SuperSU等 我们在进入市场三年后不断更新该模型的在线文件库。因此,当设备N + 3(荣誉9)开始销售时,对设备N(例如,荣誉6)的支持停止。支持状态显示在列表中。之后,我们将图像存储在服务器上1年,并在N + 4设备(荣誉10)进入市场时将它们转移到免费托管 - 它将被移动到存档模型列表,其中包含指向存档的链接。 查看列表 存档模型 链接到使用扩展Multi-Tool支持拍摄的模型文件。在链接下,您可以找到并下载库存内核(boot.img),库存恢复(recovery.img),库存eRecovery(recovery2.img),自定义TWRP恢复,自定义内核(内核),用于安装Root(SuperSu)的软件包和用于不同供应商/国家的oeminfo和用于更改区域设置的模型。 查看列表 在线基础变更 HUAWEI&HONOR应用程序的多工具在服务器上有一个支持模型的文件库。它会不断更新我们团队的成员。 结识 俄 英语 © 开发人员TEAM MT | 发展:工作室谢尔盖邦达连科

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值