网络编程之客户端通过服务器与另外一个客户端交流

news/2025/3/21 5:57:54/

服务器使用select模型搭建,客户端1使用线程搭建,客户端2使用poll模型搭建,

使用时需要先运行服务器,具体编译可看我最后的图片

 head.h头文件

#ifndef __HEAD_H_
#define __HEAD_H_

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pwd.h>
#include <dirent.h>
#include <wait.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 #include <poll.h>
#include <signal.h>
#define PRINTF_ERROR(a) {perror(a);return -1;}
#endif

 服务器搭建代码

server_select.c

#include <head.h>
enum Type{
    TYPE_LOGIN,
    TYPE_REDIST
};
typedef struct Pack{ //协议包
    int packsize;
    enum Type type;
    char buf[4096];
    int used;

}pack_t;   
void freeList(char** list){
    for(int i=0;list[i]!=NULL;i++){
        free(list[i]);
    }
    free(list);
}

char** analysis(pack_t pack){     //解析客户端发来的协议包
    char* buf=pack.buf;
    int readed_size=0;
    char** list = calloc(1,80);
    int i=0;//
    while(1){
        short size = *(short*)(buf+readed_size);
        if(size==0)
            break;
        readed_size+=2;
        char temp[size+1];
        memset(temp,0,size+1);
        memcpy(temp,buf+readed_size,size);
        readed_size+=size;

        list[i]=calloc(1,size+1);
        strcpy(list[i],temp);
        i++;
    }
    return list;

}
void insert_client(int* arr,int client,int* len){ //将客户端的描述符放入数组
    arr[*len]=client;
    (*len)++;
}
void remove_client(int* arr,int client,int* len){
    int i=0,j=0;
    for(i=0;i<*len;i++){
        if(arr[i]==client)
            break;
    }
    if(i==*len)
        return;
    for(j=i;j<*len;j++){
        arr[j]=arr[j+1];
    }
    arr[j]=0;
    (*len--);
}
int main(int argc, const char *argv[])
{
    if(argc<2){
        printf("请输入端口号:\n");
        return 1;
    }
    int port = atoi(argv[1]);
    int server = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in addr = {0};

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    if(bind(server,(struct sockaddr*)&addr,sizeof(addr))==-1){
        perror("bind");
        return -1;
    }
    listen(server,50);

    int  client_arr[64]={0};//存放所有客户端套接字的数组
    int arr_len=0;//记录数组的长度
    fd_set readfds;//创建一个select的监听列表
    FD_ZERO(&readfds);//初始化监听列表
    FD_SET(server,&readfds);//将服务器套接字放入监听列表中
    FD_SET(STDIN_FILENO,&readfds);//将标准输入流放入监听列表中

    while(1){
        fd_set temp=readfds;
        select(1024,&temp,0,0,0);
        if(FD_ISSET(0,&temp)){  输入流激活
            char buf[1024]="";
            scanf("%s",buf);
            getchar();
            printf("键盘输入数据:%s\n",buf);
        }
        if(FD_ISSET(server,&temp)){  服务器激活
            int client=accept(server,0,0);
            printf("有客户端链接\n");
            FD_SET(client,&readfds);
            insert_client(client_arr,client,&arr_len);
        }
        for(int i=0;i<arr_len;i++){
            int client = client_arr[i];
            if(FD_ISSET(client,&temp)){ //客户端激活

                pack_t pack={0};
                int size=0;
                int res=read(client,&size,4);
                if(res ==0){
                    printf("客户端断开链接\n");
                    FD_CLR(client,&readfds);
                    remove_client(client_arr,client,&arr_len);
                    close(client);
                    break;
                }
                pack.packsize=size;
                read(client,(char*)&pack+4,size-4);
                char** list=analysis(pack);
                for(int j=0;j<arr_len;j++){    
                    if(client_arr[i]!=client_arr[j]){
                        if(write(client_arr[j],&pack,size)<=0)
                            PRINTF_ERROR("write error");
                    }
                }
                printf("%s\n",list[0]);
                freeList(list);

            }
        }
    }
    return 0;
}

 客户端1

client1.c

#include <head.h>
#include <pthread.h>
enum Type{
    TYPE_REGIST,
    TYPE_LOGIN
};

typedef struct Pack{
    int packsize;
    enum Type type;
    char buf[4096];
    int used;
}pack_t;

void append(pack_t* pack,const char* str){
    char* buf=pack->buf;
    short size=strlen(str);
    *(short*)(buf+pack->used)=size;
    pack->used+=2;
    memcpy(buf+pack->used,str,size);
    pack->used+=size;
    pack->packsize=pack->used+8;
}
void freeList(char** list){
    for(int i=0;list[i]!=NULL;i++){
        free(list[i]);
    }                                                  
    free(list);
}

char** analysis(pack_t pack){
    char* buf=pack.buf;
    int readed_size=0;
    char** list = calloc(1,80);
    int i=0;//
    while(1){
        short size = *(short*)(buf+readed_size);
        if(size==0)
            break;
        readed_size+=2;
        char temp[size+1];
        memset(temp,0,size+1);
        memcpy(temp,buf+readed_size,size);
        readed_size+=size;

        list[i]=calloc(1,size+1);
        strcpy(list[i],temp);
        i++;
    }
    return list;

}


void* Write(void* arg){
    int client =*(int *)arg;
    while(1){
        char str[20]="";
        pack_t pack={0};
        pack.type=TYPE_REGIST;
        scanf("%19s",str);
        getchar();
        append(&pack,str);
        write(client,&pack,pack.packsize);
    }
    pthread_exit(NULL);

}
void* Read(void* arg){
    int client =*(int *)arg;
    while(1){
        pack_t pack1={0};
        int size=0;
        read(client,&size,4);
        read(client,(char*)&pack1+4,size-4);
        char** list=analysis(pack1);
        printf("客户端1:%s\n",list[0]);
    
        freeList(list);


    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    if(argc<2){
        printf("请输入端口号\n");
        return -1;
    }
    int port=atoi(argv[1]);
    int client=socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in addr={0};
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr("0.0.0.0");
    if(connect(client,(struct sockaddr*)&addr,sizeof(addr))==-1){
        PRINTF_ERROR("bind error");
    }

    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,Write,&client);
    pthread_create(&tid2,NULL,Read,&client);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);


    return 0;
}

客户端2

client2.c

#include <head.h>
enum Type{
    TYPE_LOGIN,
    TYPE_REDIST
};
typedef struct Pack{
    int packsize;
    enum Type type;
    char buf[4096];
    int used;

}pack_t;
void append(pack_t* pack,const char* str){
    char* buf = pack->buf;
    short size = strlen(str);
    *(short*)(buf+pack->used) = size;
    pack->used +=2;
    memcpy(buf+pack->used,str,size);
    pack->used+=size;
    pack->packsize=pack->used+8;
}
// 该函数功能为:将client存入数组arr中的最后一个下标位置上,存完之后,arr数组的长度记得自增
void insert_client(struct pollfd* arr,struct pollfd client,int* len){
    arr[*len] = client;
    (*len)++;
}

// 将client从数组arr中移除,移除后记得数组长度-1
void remove_client(struct pollfd* arr,int client,int* len){
    int i = 0,j = 0;
    for(i=0;i<*len;i++){
        if(arr[i].fd == client){break;}
    }                                                                                       
    if(i == *len){return;}
    for(j=i;j<*len-1;j++){
        arr[j] = arr[j+1];
    }
    //arr[j] = 0;
    (*len)--;
}
void freeList(char** list){
    for(int i=0;list[i]!=NULL;i++){
        free(list[i]);
    }
    free(list);
}

char** analysis(pack_t pack){
    char* buf=pack.buf;
    int readed_size=0;
    char** list = calloc(1,80);
    int i=0;//
    while(1){
        short size = *(short*)(buf+readed_size);
        if(size==0)
            break;
        readed_size+=2;
        char temp[size+1];
        memset(temp,0,size+1);
        memcpy(temp,buf+readed_size,size);
        readed_size+=size;

        list[i]=calloc(1,size+1);
        strcpy(list[i],temp);
        i++;
    }
    return list;

}

int main(int argc, const char *argv[])
{
    if(argc<2){
        printf("请输入端口号:\n");
        return 1;                   
    }
    int port = atoi(argv[1]);

    int client = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    if(connect(client,(struct sockaddr*)&addr,sizeof(addr))==-1){
        perror("connect");
        return -1;
    }

    struct pollfd list[50]={0};
    int list_len=0;
    struct pollfd client_fd={fd:client,events:POLLIN,revents:0};
    struct pollfd stdin_fd={fd:0,events:POLLIN,revents:0};

    insert_client(list,client_fd,&list_len);

    insert_client(list,stdin_fd,&list_len);


    while(1){

        poll(list,list_len,-1);
        int i=0;
        for(i=0;i<list_len;i++){
            if(list[i].revents==0){
                continue;
            }
            int fd=list[i].fd;
            if(fd==0){
                char str[20]="";
                pack_t pack = {0};
                pack.type=TYPE_REDIST;
                scanf("%19s",str);
                getchar();
                append(&pack,str);
                write(client,&pack,pack.packsize);
            }else{
                pack_t pack1={0};
                int size=0;
                read(client,&size,4);
                read(client,(char*)&pack1+4,size-4);
                char** list=analysis(pack1);
                printf("客户端2:%s\n",list[0]);
                freeList(list);

            }
        }
    }

    return 0;
}

 结果


http://www.ppmy.cn/news/1580789.html

相关文章

基于 Python 爬取 TikTok 搜索数据 Tiktok爬虫(2025.3.17)

1. 前言 在数据分析和网络爬虫的应用场景中&#xff0c;我们经常需要获取社交媒体平台的数据&#xff0c;例如 TikTok。本篇文章介绍如何使用 Python 爬取 TikTok 用户搜索数据&#xff0c;并解析其返回的数据。 结果截图 2. 项目环境准备 在正式运行代码之前&#xff0c;我…

K8s集群的环境部署

1.测试环境所需要的主机名和IP和扮演的角色 harbor 172.25.254.200 harbor仓库 k8s-master 172.25.254.100 k8s集群控制节点 k8s-node1 172.25.254.10 k8s集群工作节点 k8s-node2 172.25.254.20 k8集群工作节点 注意&#xff1a;所有节点禁用selinux和防火墙 所有节点同步…

【Linux我做主】基础命令完全指南上篇

Linux基础命令完全指南【上篇】 Linux基础命令完全指南github地址前言命令行操作的引入Linux文件系统树形结构的根文件系统绝对路径和相对路径适用场景Linux目录下的隐藏文件 基本指令目录和文件相关1. ls2. cd和pwdcdpwd 3. touch4. mkdir5. cp6. mv移动目录时覆盖写入的两种特…

React第三十章(css原子化)

原子化 css 什么是原子化 css 原子化 CSS 是一种现代 CSS 开发方法&#xff0c;它将 CSS 样式拆分成最小的、单一功能的类。比如一个类只负责设置颜色&#xff0c;另一个类只负责设置边距。这种方式让样式更容易维护和复用&#xff0c;能提高开发效率&#xff0c;减少代码冗余…

Python----计算机视觉处理(Opencv:形态学变换)

一、形态学变化 形态学变换&#xff08;Morphological Transformations&#xff09;是一种基于形状的图像处理技术&#xff0c;主要处理的对象为二值化图像。 形态学变换有两个输入和一个输出&#xff1a;输入为原始图像和核&#xff08;即结构化元素&#xff09;&#xff0c;输…

【Leetcode刷题随笔】206.反转链表

1.题目简介 翻转一个单链表&#xff0c;示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL。 原题链接&#xff1a;206.反转链表. 2.解法思路 要反转一个链表&#xff0c;可以定义一个新的链表来实现反转&#xff0c;但是内存空间消…

区块链加密技术公司DApp开发指南:从零开始到上线

随着区块链技术的普及&#xff0c;去中心化应用&#xff08;DApp&#xff09;成为加密技术公司探索的核心领域。本文结合行业实践与最新技术趋势&#xff0c;系统梳理DApp从需求分析到上线的完整开发流程&#xff0c;并融入关键工具、安全策略与案例解析&#xff0c;助力企业高…

Elasticsearch text字段检索方法

在 Elasticsearch 中,text 类型的字段会默认被分词(分析处理),适合全文检索。以下是针对 name(text 类型)的常见操作示例: 1. 全文检索(Match Query) 匹配分词后的任意词项,适合模糊搜索: GET /your_index/_search {"query": {"match": {&quo…