Linux C利用Socket套接字进行服务器与多个客户端进行通讯

Linux--借助消息队列,模拟实现服务器与多个客户端之间独立发送消息 假设有一个服务器,有多个客户端访问它,怎么借助消息队列,让它把a客户端的回复消息只发给a,而b客户端的回复消息只发给b。 首先是服务器程序: #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/msg.h> #include<signal.h> str 阅读详情

代码思路:

服务器端:首先创建监听的套接字,然后创建用于(循环检测客户端连接)的新线程,当检测到有客户端连接,则创建一个新的线程负责同这个套接字通讯,即接受这个套接字发送过来的消息。

 

现象:

单个客户端同服务器可以进行通讯,当连接上多个客户端过后,服务器端能够群发消息,最后连接的客户端向服务器发送的消息能够接受到,但之前的所有的客户端向服务器发送消息均不能收到。

 

解决思路:

先使用排除法,首先找到了一份正确的客户端代码,连接上我自己写的服务器,也出现了同样的错误,所以可以排除我自己写的客户端。最终到了服务器端。服务器中,因为单个客户端可以与服务器通讯,所以检测连接的线程没有问题,那么最终归结到了循环接受客户端数据的线程。那着正确的服务器接受客户端数据的代码同自己写的对照,哈哈,发现了问题,是_socketCon值的问题,_socketCon的赋值是通过指针,然后打印这个值,发现这个值在不断变化,并且在检测连接的线程中定义的 int socketCon 值正是这个,而这个值是在 while死循环中不停的创建,不停的销毁!哇咔咔,真相终于大白与天下,功夫不负有心人啊,查了一天半的bug终于被我查出来了!

总结:

1、在死循环中切记不要定义变量,最好是在死循环之外定义变量,在死循环中使用!

2、在函数有死循环时,值只有一个的局部变量,就在死循环之上定义并初始化!就不要在死循环中赋值,只是去使用它就好了!

3、在创建多线程给多线程处理函数传参数中,切记不要传入生命周期较短的指针变量,传入值类型都好!

 

服务器端故障代码如下

void *fun_thrReceiveHandler(void *socketCon){
    char buffer[30];
    int buffer_length;
    int _socketCon;
    while(1){
        //添加对buffer清零
        bzero(&buffer,sizeof(buffer));
        _socketCon = *((int *)socketCon);
        sleep(0.1);
        printf("接受数据线程中,连接套接字为%d\n",_socketCon);
        
        buffer_length = read(_socketCon,buffer,30);
        if(buffer_length == 0){
            printf("%d 客户端关闭\n",_socketCon);
            conClientCount--;
            break;
        }else if(buffer_length < 0){
            printf("接受客户端数据失败\n");
            break;
        }
        buffer[buffer_length] = '\0';
        printf("%d 说:%s\n",_socketCon,buffer);
        //获取当前线程id
        //printf("当前线程id:%ld",pthread_self());
        sleep(0.2);
    }
    printf("接受数据线程结束了\n");
    return NULL;
}

服务器与多个客户端通讯的正确代码如下:

服务器端 - socket_server.c

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
//服务器端

void *fun_thrReceiveHandler(void *socketInfo);
void *fun_thrAcceptHandler(void *socketListen);
//1:是 0:否
int checkThrIsKill(pthread_t thr);

typedef struct MySocketInfo{
    int socketCon;
    char *ipaddr;
    uint16_t port;
}_MySocketInfo;

// 客户端数组
struct MySocketInfo arrConSocket[10];
int conClientCount = 0;

// 接受客户端线程列表
pthread_t arrThrReceiveClient[10];
int thrReceiveClientCount = 0;

int main()
{
    //初始化全局变量
    //memset(arrConSocket,0,sizeof(struct MySocketInfo)*10);

    printf("开始socket\n");
    /* 创建TCP连接的Socket套接字 */
    int socketListen = socket(AF_INET, SOCK_STREAM, 0);
    if(socketListen < 0){
        printf("创建TCP套接字失败\n");
        exit(-1);
    }else{
        printf("创建套接字成功\n");
    }
    /* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */
    server_addr.sin_port=htons(2000);
    if(bind(socketListen, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) != 0){
        perror("绑定ip地址、端口号失败\n");
        exit(-1);
    }else{
        printf("绑定ip地址,端口号\n");
    }
    /* 开始监听相应的端口 */
    if(listen(socketListen, 10) != 0){
        printf("开启监听失败\n");
        exit(-1);
    }else{
        printf("开启监听成功\n");
    }
    /* 接受连接套接字 */
    pthread_t thrAccept;
    pthread_create(&thrAccept,NULL,fun_thrAcceptHandler,&socketListen);

    /* 实时发送数据 */
    while(1){
        //判断线程存活多少
        int i;
        for(i=0;i<thrReceiveClientCount;i++){
            if(checkThrIsKill(arrThrReceiveClient[i]) == 1){
                printf("有个线程被杀了\n");
                thrReceiveClientCount--;
            }
        }
        printf("当前有接受数据线程多少个:%d\n",thrReceiveClientCount);

        // 可以录入用户操作选项,并进行相应操作
        char userStr[30] = {'0'};
        scanf("%s",userStr);
        if(strcmp(userStr,"q") == 0){
            printf("用户选择退出!\n");
            break;
        }
        // 发送消息
        if(conClientCount <= 0){
            printf("没有客户端连接\n");
        }else{
            int i;
            for(i=0; i<conClientCount; i++){
                //int sendMsg_len = send(arrConSocket[i].socketCon, userStr, 30, 0);
                int sendMsg_len = write(arrConSocket[i].socketCon,userStr,30);
                if(sendMsg_len > 0){
                    printf("向%s:%d发送成功\n",arrConSocket[i].ipaddr,arrConSocket[i].port);
                }else{
                    printf("向%s:%d发送失败\n",arrConSocket[i].ipaddr,arrConSocket[i].port);
                }
            }
        }

        sleep(0.5);
    }

    // 等待子进程退出
    printf("等待子线程退出,即将退出!\n");
    char *message;
    pthread_join(thrAccept,(void *)&message);
    printf("%s\n",message);

    return 0;
}

void *fun_thrAcceptHandler(void *socketListen){
    while(1){
        int sockaddr_in_size = sizeof(struct sockaddr_in);
        struct sockaddr_in client_addr;
        int _socketListen = *((int *)socketListen);
        int socketCon = accept(_socketListen, (struct sockaddr *)(&client_addr), (socklen_t *)(&sockaddr_in_size));
        if(socketCon < 0){
            printf("连接失败\n");
        }else{
            printf("连接成功 ip: %s:%d\r\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
        }
        printf("连接套接字为:%d\n",socketCon);
        //开启新的通讯线程,负责同连接上来的客户端进行通讯
        _MySocketInfo socketInfo;
        socketInfo.socketCon = socketCon;
        socketInfo.ipaddr = inet_ntoa(client_addr.sin_addr);
        socketInfo.port = client_addr.sin_port;
        arrConSocket[conClientCount] = socketInfo;
        conClientCount++;
        printf("连接了%d个用户\n",conClientCount);

        pthread_t thrReceive = 0;
        pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketInfo);
        arrThrReceiveClient[thrReceiveClientCount] = thrReceive;
        thrReceiveClientCount++;

        //让进程休息1秒
        sleep(0.5);
    }

    char *s = "安全退出接受进程";
    pthread_exit(s);
}

void *fun_thrReceiveHandler(void *socketInfo){
	char buffer[30];
	int buffer_length;
	_MySocketInfo _socketInfo = *((_MySocketInfo *)socketInfo);
    while(1){
    	//添加对buffer清零
    	bzero(&buffer,sizeof(buffer));

        buffer_length = read(_socketInfo.socketCon,buffer,30);
        if(buffer_length == 0){
            printf("%s:%d 客户端关闭\n",_socketInfo.ipaddr,_socketInfo.port);
            conClientCount--;
            break;
        }else if(buffer_length < 0){
            printf("接受客户端数据失败\n");
            break;
        }
        buffer[buffer_length] = '\0';
        printf("%s:%d 说:%s\n",_socketInfo.ipaddr,_socketInfo.port,buffer);

        sleep(0.2);
    }
    printf("接受数据线程结束了\n");
    return NULL;
}

int checkThrIsKill(pthread_t thr){
    int res = 1;
    int res_kill = pthread_kill(thr,0);
    if(res_kill == 0){
        res = 0;
    }
    return res;
}

客户端 - socket_client.c

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

typedef struct MySocketInfo{
    int socketCon;
    unsigned long ipaddr;
    unsigned short port;
}_MySocketInfo;

void *fun_thrReceiveHandler(void *socketCon);
int checkThrIsKill(pthread_t thr);

int main()
{
    printf("开始socket\n");
    /* 创建TCP连接的Socket套接字 */
    int socketCon = socket(AF_INET, SOCK_STREAM, 0);
    if(socketCon < 0){
        printf("创建TCP连接套接字失败\n");
        exit(-1);
    }
    /* 填充客户端端口地址信息,以便下面使用此地址和端口监听 */
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); /* 这里地址使用全0,即所有 */
    server_addr.sin_port=htons(2000);
    /* 连接服务器 */
    int res_con = connect(socketCon,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
    if(res_con != 0){
        printf("连接失败\n");
        exit(-1);
    }
    printf("连接成功,连接结果为:%d\n",res_con);
    //开启新的实时接受数据线程
    pthread_t thrReceive;
    pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon);

    /* 实时发送数据 */
    while(1){
        //检测接受服务器数据线程是否被杀死


        char userStr[30] = {'0'};
        // 可以录入用户操作选项,并进行相应操作
        scanf("%s",userStr);
        if(strcmp(userStr,"q") == 0){
            printf("用户选择退出!\n");
            break;
        }
        // 发送消息
        //int sendMsg_len = send(socketCon, userStr, 30, 0);
        int sendMsg_len = write(socketCon,userStr,30);
        if(sendMsg_len > 0){
            printf("发送成功,服务端套接字句柄:%d\n",socketCon);
        }else{
            printf("发送失败\n");
        }

        //if(checkThrIsKill(thrReceive) == 1){
            //printf("接受服务器数据的线程已被关闭,退出程序\n");
            //break;
        //}
    }
    // 关闭套接字
    close(socketCon);
    return 0;
}

void *fun_thrReceiveHandler(void *socketCon){
    while(1){
        char buffer[30];
        int _socketCon = *((int *)socketCon);
        //int buffer_length = recv(_socketCon,buffer,30,0);
        int buffer_length = read(_socketCon,buffer,30);
        if(buffer_length == 0){
            printf("服务器端异常关闭\n");
            exit(-1);
        }else if(buffer_length < 0){
            printf("接受客户端数据失败\n");
            break;
        }
        buffer[buffer_length] = '\0';
        printf("服务器说:%s\n",buffer);
    }
    printf("退出接受服务器数据线程\n");
    return NULL;
}

int checkThrIsKill(pthread_t thr){
    int res = 1;
    int res_kill = pthread_kill(thr,0);
    if(res_kill == 0){
        res = 0;
    }
    return res;
}

编译 - Makefile

all:
	$(CC) socket_server.c -lpthread -o socket_server
	gcc socket_client.c -lpthread -o socket_client
clean:
	rm socket_server socket_client

github地址:https://github.com/scJingP/linux-rt-test

Linux网络编程【一】:TCP socket套接字详解 1.socket socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为 socket。 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成 的socketpair就唯一标识一个连接。 socket本身 有“插座”的意思,因此用来描述网络连接的一 对一关系 阅读详情

相关推荐

嵌入式Linux-Socket套接字通信-Class_1

在实验过程中,虽然遇到了各种问题,但通过不断排查和解决,成功完成了各项实验任务,包括使用 “TCP 调试助手” 进行通信测试、运行 “linux - socket - C - S” 代码实现简单的客户端与服务器通信、分析教材第九章代码理解 TCP 和 UDP 通信原理,以及编写能够接收和处理 JSON 数据的 socket 服务端程序。”目录中代码对应的socket客户端发来的json数据,结合代码中json数据格式的内容,编写接收json数据的。在实验中做了什么事情,怎么做的,发生的现象和中间结果)

m0_73939014的博客 934

lwip 一个服务器连接多个客户端_【网络】Linux TCP客户端与服务器的C代码实现

在计算机网络的编程中,OSI参考模型中规定了各层的定义与功能。针对于其中的网络层,传输层,以及应用层这三层,整体称之为网络协议栈,常见有PC平台的TCP/IP,嵌入式平台的lwIP,CycloneTCP等。关于数据链路层和物理层,这是硬件层面的协议,由网卡和MAC/LLC去实现。本文中的TCP server 和 client,为基于软件层面(TCP/IP协议栈)的Socket应用开发。1 Sock...

weixin_39537977的博客 3043

c语言socket多线程编程限制客户端连接数

主要介绍了c语言socket多线程编程,可以限制客户端连接数,大家参考使用吧

tcp_pthread的搭建(个人学习记录)

【代码】tcp_pthread的搭建(个人学习记录)

LLL126345的博客 107

最佳解决Maven同一依赖多版本共存问题,重复依赖(同一个jar包,多个版本)-maven-shade-plugin

公司的历史项目在导出功能上引用的是poi-3.12版本,后续再做word模板导出功能时用到了poi-3.15版本,升级完后发现项目的很多导出功能都瘫痪了。用旧版本,新功能无法使用;用新版本,旧功能瘫痪,且整改的工作量比较大。使用maven-shade-plugin,将某个版本的依赖名称进行修改,重新引入即可。

S_White_Island的博客 1万+

大麦APP抢票协议分析:从‘掌密网络’代码看移动端API安全防护

本文深度解析了大麦APP抢票协议的安全防护体系,重点分析了'掌密网络'代码中体现的动态签名、设备指纹和人机验证等关键技术。文章详细介绍了大麦APP如何通过多层次API安全防护应对抢票压力,包括参数加密、请求头部设计和异常处理机制,为移动应用开发者提供了安全实践建议。

weixin_28740299的博客 289

socket--Linux下的广播

Linux使用socket实现广播

qq_42425882的博客 1712

ENVI基于Landsat影像构建郑州市2000-2019年遥感生态指数RSEI

本文基于landsat5、landsat7、landsat8三种传感器的影像,使用ENVI软件构建郑州市2000-2019年遥感生态指数RSEI。内含ENVI详细操作、使用的公式以及构建结果。

Lexicopperfield的博客 1万+

linux下socket实现多个客户端与服务器的通信

学习完《UNIX环境高级编程》套接字一章的内容之后,自己实现了单个客户端与服务器的通信程序,后面想想要是多个客户端如何与服务器通信呢?这就有了这篇文章。 这里采用的是用多线程实现多客户端与服务器的通信,多线程的思路参考了Linux C利用Socket套接字进行服务器与多个客户端进行通讯,在此感谢原文章作者。 服务器端程序: #include #include #inclu

wdlove58的专栏 1万+

OpenHarmony-AI调研

基于OpenHarmony-5.0.0版本以及laval社区等网络资料调研当前OH上的AI发展情况。

qqq1112345的博客 1369

玩转Matlab-Simscape(初级)-01-从一个简单模型开始学习之旅

本文从初学者的角度分享的关于Matlab simscape 的基础内容,简单分享了Simscape搭建模型,创建子系统,修改定义参数等内容,为将来深入学习和使用simscape做铺垫。

万金油笑匠的博客 6517

Linux环境下使用c++通过Socket套接字实现CAN通讯

本文介绍了如何在Linux环境下,使用C语言通过Socket套接字实现CAN协议通讯。对Socket套接字中需要使用的函数、传参及函数返回值等细节问题做出了详细的解释。

weixin_45496000的博客 3832

AUTOSAR SHE 密钥更新协议

依赖内存槽的使用用例,内存槽有不同的保护协议。M2是新的计数值C_ID‘,相应的标志位F_ID',0填充第一个块,以及新的密钥K_ID'的组合的CBC算法加密的消息。例如,我要更新一个MASTER_ECU_KEY, 那么参考上表,我需要知晓Non-volatile的MASTER_ECU_KEY (即授权密文为MASTER_ECU_KEY自己的密文).内存的更新协议分为两部分,第一部分是实际内存更新到发送一个秘密且真实的key到SHE,然后第二部分为验证,即提供给后台一个key更新成功的证据。

weixin_46481662的博客 3277

linux c socket 服务器 客户端 简单通讯

1.socket简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都...

whatday的专栏 1388

黑猫带你学eMMC协议第22篇:emmc可靠写详解(Write Reliability)

1 前言 1.1 声明 本文依据eMMC JEDEC5.1、网络资料及个人工作经验整理而成,如有部分错误请留言。 本文部分内容可能源自网络,侵删。 本文结合eMMC JEDEC5.1协议手册查看效果更佳。 1.2 内容提要 1、什么是可靠性写属性(Write Reliability)? 2、可靠写和普通的写有什么区别? 3、设置可靠写的流程及流程框图。 4、可靠写需要遵循哪些规则? 1.3 参考链接 本文主要参考链接:http://www.up48.com/cn/support/8_37.html 2 什么

黑猫奥利奥的博客 3122

c\c++: Identifiers, modifier, qualifier, specifier, decorator; systemtap

文章目录参考英语含义在c、C++ 参考 c++ 标准 https://blog.youkuaiyun.com/tyronne/article/details/44016621 英语含义 modifier word or phrase that modifies (modify 2) another word or phrase 修饰语. qualifier (grammar) word, esp an adjective or adverb, that qualifies another word 修饰词; (尤指)形

mzhan017的博客 2427

Linux下基于TCP协议的Socket套接字编程(客户端&服务端)入门详解

本文为基于TCP协议的Socket网络通信编程入门级介绍,详细介绍客户端、服务端使用socket 套接字进行通信的详细流程、接口讲解、代码示例、实际运行效果等。

weixin_42307601的博客 1万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

C/C++ Linux Socket网络编程 TCP 与 UDP

详细介绍Linux环境C/C++如何使用socket通信,TCP与UDP;

cpp_learner的博客 1万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

计算机网络 | socket IPC(本地套接字domain)

socket IPC介绍,本地套接字的通信机制,以及server和client的代码实现。

仅以此博客记录日常学习工作中所思所得,水平有限,难登大雅,万望海涵。 3213

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Socket编程详解

Socket中文意思是“插座”,在Linux环境下,用于表示进程x间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是文件主要应用于本地持久化数据的读写,而套接字多应用于网络进程间数据的传递。在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一

qq_51654808的博客 2万+

第十二篇:linux下socket本地套接字通讯

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

wh_xia_jun的专栏 859

Linux 网络编程:使用Socket套接字实现IPC进程间通讯

Socket IPC结合了网络编程的灵活性和本地通信的高效性,是Linux系统编程中的重要工具。通过本文的学习,你不仅掌握了Socket IPC的实现方法,还了解了各种IPC方式的适用场景。在实际项目中,根据具体需求选择合适的IPC方式,往往能达到事半功倍的效果。关注我,持续更新 Linux/C++/网络编程干货!

buyutang_的博客 1007

linux socke 通信程序,linux下socket实现多个客户端与服务器的通信

学习完《UNIX环境高级编程》套接字一章的内容之后,自己实现了单个客户端与服务器的通信程序,后面想想要是多个客户端如何与服务器通信呢?这就有了这篇文章。这里采用的是用多线程实现多客户端与服务器的通信,多线程的思路参考了Linux C利用Socket套接字进行服务器与多个客户端进行通讯,在此感谢原文章作者。服务器端程序:#include#include #include #include #incl...

weixin_33573700的博客 587

【Linux网络】Linux网络编程套接字,UDP与TCP

在当今这个信息技术日新月异的时代,网络编程已成为连接世界、构建各类互联网应用不可或缺的一部分。而Linux,作为开源操作系统的典范,其强大的网络功能和灵活性,为开发者们提供了一个广阔而深入的实践平台。在众多网络编程技术中,套接字(Socket)编程无疑是核心与基石,它不仅支撑着Web服务、即时通讯、在线游戏等日常应用,还是实现分布式系统、云计算服务的关键技术之一。

EterNity_TiMe_的计算机学习之路 5619

Linux服务器、客户端通信套接字代码超详解

服务器程序: #include<stdio.h>//输入输出 #include<stdlib.h>//动态内存分配 #include<string.h> //linux下有这个头文件,vc下没有 //它包含很多UNIX系统服务的函数原型,eg:read write函数等 #include<unistd.h> #include<errno.h> #include<pthread.h>//包含有关多线程的函数 #include &lt

weixin_45479946的博客 482
上一篇: iOS第三方插件与App Demo地址总览
下一篇: linux c 实现 ls 命令
returningprodigal
博客等级 码龄12年 53粉丝 122原创
评论 29
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值