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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/585095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单片机编程实例400例大全(1-100)

最近有一些新手&#xff0c;咨询我去实现某个功能&#xff0c;没思路&#xff0c;无从下手&#xff0c;怎么办&#xff1f; 平时太忙&#xff0c;没时间一一解答&#xff0c;今天发篇文说下。 这是每个人必经的阶段&#xff0c;不必自责和焦虑。 我是如何解决这个问题的&#x…

Postman 汉化安装及使用指南:快速上手 Postman 中文版

Postman 是一款常用的 API 测试工具&#xff0c;可以方便地进行接口测试、调试和文档编写。本文将详细介绍如何下载安装 Postman 并汉化&#xff0c;包括每个步骤的详细说明。 下载安装 Postman 1、打开浏览器&#xff0c;访问 Postman 官网&#xff0c;下载适用于自己系统的…

(el-Transfer)操作(不使用 ts):Element-plus 中 Transfer 穿梭框右侧数据不展示的问题

Ⅰ、Element-plus 提供的Transfer树形控件组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供Transfer组件情况&#xff1a; 其一、Element-ui 自提供的Transfer代码情况为(示例的代码)&#xff1a; <template><p style"text-align: center; margin: …

Nacos原理-2024

文章目录 1. 什么是Nacos2. 注册中心原理3. 配置中心原理 1. 什么是Nacos Nacos注册中心分为server与client&#xff0c;server采用Java编写&#xff0c;为client提供注册发现服务与配置服务。而client可以用多语言实现&#xff0c;client与微服务嵌套在一起&#xff0c;nacos…

让大模型prompt生成Mermaid流程图

生成内容、总结文章让大模型Mermaid流程图展示&#xff1a; mermaid 美人鱼, 是一个类似 markdown&#xff0c;用文本语法来描述文档图形(流程图、 时序图、甘特图)的工具&#xff0c;您可以在文档中嵌入一段 mermaid 文本来生成 SVG 形式的图形 kimi效果示例&#xff1a; 使用…

【算法一则】【动态规划】求二维数组可组成的最大正方形

题目 在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内&#xff0c;找到只包含 ‘1’ 的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],["1","0&…

大模型应用开发极简入门

简单的归纳一下书的前序部分 目录 LLM&#xff08;Large Language Model&#xff09;的应用技术栈通常包括以下几个方面&#xff1a; 深度学习框架&#xff1a; 数据预处理工具&#xff1a; 训练资源&#xff1a; 模型优化和调参工具&#xff1a; 部署和应用集成&#xf…

最新AI实景无人自动直播软件:一部手机就可以实现无人直播;商业拓客带货的必备利器

智享实景无人直播系统在商业拓展中的作用不可忽视。本文将探讨该系统的特点和优势&#xff0c;展示其省时省力的优势以及在商家拓客和源头公司项目招商中的关键作用。 随着人工智能技术的飞速发展&#xff0c;智能化解决方案正逐渐渗透到各行业&#xff0c;在商业拓展领域取得了…

刷题训练之位运算

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握位运算算法。 > 毒鸡汤&#xff1a;学习&#xff0c;学习&#xff0c;再学习 ! 学&#xff0c;然后知不足。 > 专栏选自&#xff1a;刷题…

数据库分库分表

数据库分库分表 分库分表到底是什么 分库分表其实是分库,分表,分库分表的总称 分库 将数据按照一定规则存储到不同的数据库中,每个数据库存储一部分数据 分库主要解决的是并发量过大的问题&#xff0c;并发量一旦上升&#xff0c;那么数据库就可能成为系统的瓶颈&#xff…

综合性练习(后端代码练习2)——用户登录

目录 一、准备工作 二、约定前后端交互接口 1、需求分析 2、接口定义 1、输入账户密码界面 2、当前登录的用户界面 三、实现服务端代码 四、调整前端页面代码 1、login.html代码&#xff1a; 页面跳转的三种方式&#xff1a; 2、index.html代码&#xff1a; 五、运…

[华为OD] C卷 服务器cpu交换 现有两组服务器QA和B,每组有多个算力不同的CPU 100

题目&#xff1a; 现有两组服务器QA和B,每组有多个算力不同的CPU,其中A[i]是A组第i个CPU的运算能 力&#xff0c;B[i]是B组第i个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。 为了让两组服务器的算力相等&#xff0c;允许从每组各选出一个CPU进行一次交换。 求两…

计算机网络----第十三天

DNS协议和文件传输协议 DNS&#xff1a; 含义&#xff1a;用于域名和IP地址的互相解析 DNS域名&#xff1a; 背景&#xff1a;通过IP地址访问目标主机&#xff0c;不便于记忆 域名的树形层次化结构&#xff1a; ①根域 ②顶级域&#xff1a;主机所处的国家/区域&#xf…

个人学习资源整理

文章目录 视频相关stl源码讲解相关 网站相关CPP网站 视频相关 stl源码讲解相关 跳转 网站相关 CPP网站 https://cplusplus.com/ https://gcc.gnu.org/

PostgreSQL的扩展(extensions)-常用的扩展之pg_repack

PostgreSQL的扩展&#xff08;extensions&#xff09;-常用的扩展之pg_repack pg_repack 是一款非常有用的 PostgreSQL 扩展工具&#xff0c;它能够重新打包&#xff08;repack&#xff09;表和索引以回收空间并减少碎片&#xff0c;而且在这个过程中不会锁定表&#xff0c;允…

软件测试常问的超高频面试题目,2022最强版,附答案

1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己&…

基于Vue3的Axios异步请求

基于Vue3的Axios异步请求 1. Axios安装与应用2. Axios网络请求封装3. axios网络请求跨域前端解决方案server.proxy 1. Axios安装与应用 Axios是一个基于promise的网络请求库&#xff0c;Axios.js.中文文档&#xff1a;https://axios.js.cn/ 安装&#xff1a;npm install --sa…

Apollo Dreamview+之Studio插件安装

步骤一&#xff1a;登录 Apollo Studio 工作台 登录 Apollo Studio 工作台。 步骤二&#xff1a;获取插件安装链接 在账户信息中&#xff0c;单击 我的服务 。 2. 选择 仿真 页签。 3. 在 插件安装 中单击 生成 &#xff0c;选择 Apollo 最新版本&#xff0c;并单击 确定 。…

计算机视觉大项目(1)-水果分级系统

项目来源&#xff1a;河北大学计算机视觉课程-杨老师. 一共有四个标题&#xff0c;本篇博客只完成前两问。 目录 实验目的: 实验内容&#xff1a; 实验步骤&#xff1a; 1.水果图像的分割 >掩膜图像Mask 是什么&#xff1f; >改进:去除反光部分的影响 2&#xf…

ES6之rest参数、扩展运算符

文章目录 前言一、rest参数二、扩展运算符 1.将数组转化为逗号分隔的参数序列2.应用总结 前言 rest参数与arguments变量相似。ES6引入rest参数代替arguments&#xff0c;获取函数实参。扩展运算符能将数组转化为参数序列。 一、rest参数 function namelist1() {console.log(ar…
最新文章