C语言 网络编程(十四)并发的TCP服务端-以线程完成功能

简介: 这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。

网络(十四)并发的TCP服务端-以线程完成功能

服务端代码

// todo TCP多线程服务端程序 循环接收客户端数据,将数据回传,解决粘包问题,不定长的数据传输
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#define N 128

//初始化socket
int  init_socket(char *ip,char *port){
   
    int init_socket_fd= socket(AF_INET,SOCK_STREAM,0);
    if (init_socket_fd==-1){
   
        printf("init_socket err");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in server_addr;
    socklen_t len=sizeof(server_addr);
    bzero(&server_addr,len);
    server_addr.sin_family=AF_INET;
    inet_aton(ip,&server_addr.sin_addr);
    server_addr.sin_port= htons(atoi(port));

    int bind_ret= bind(init_socket_fd,(struct sockaddr*)&server_addr,len);
    if (bind_ret == -1) {
   
        printf("bind error\n");
        exit(EXIT_FAILURE);
    }

    int listen_ret= listen(init_socket_fd,10);
    if (listen_ret == -1) {
   
        printf("listen error\n");
        exit(EXIT_FAILURE);
    }


    return init_socket_fd;
}

//客户端发送消息
int  Server_Send_data(int clientFD,char* msg){
   
    strcat(msg,"-回传");


    int server_send_len=send(clientFD,msg,strlen(msg),0);
    if (server_send_len == -1) {
   
        printf("send error\n");
        exit(EXIT_FAILURE);
    }if (server_send_len == 0) {
   
        printf("客户端关闭连接\n");
        return -1;
    }
    printf("发送给客户端数据:[%s]\n",msg);
    return 0;
}


//接收数据
int Server_Receive_data(int clientFD){
   
    while (1){
   
        //接收-使用新的文件描述符
        //先接收四字节的长度
        int msg_len=0;
        int total_received=0;

        int recv_len=recv(clientFD,&msg_len,4,0);
        if (recv_len == -1) {
   
            printf("recv error\n");
            exit(EXIT_FAILURE);
        }
        if (recv_len == 0) {
   
            printf("客户端关闭连接\n");
            break;
        }

        while (1){
   
            char recv_buf[N];
            bzero(recv_buf,N);
            recv_len = recv(clientFD, recv_buf+total_received, msg_len-total_received, 0);
            if (recv_len == -1) {
   
                printf("recv error\n");
                exit(EXIT_FAILURE);
            }
            if (recv_len == 0) {
   
                break;
            }
            if (strncmp(recv_buf, "exit", 4) == 0) {
   
                printf("客户端退出通信\n");
                close(clientFD);
                return 0;
            }
            total_received+=recv_len;
            printf("收到客户端消息:|%s|\n",recv_buf);

            //Server_Send_data(clientFD, recv_buf);
        }

    }
    return 0;
}

void* do_client(void* cfd){
   
    int cfd_int=*(int*)cfd;
    Server_Receive_data(cfd_int);
    close(cfd_int);
    pthread_exit(NULL);
}



int main(){
   

    int socket_fd = init_socket("172.17.140.183","8080");

    struct sockaddr_in cli_addr;
    socklen_t cli_len=sizeof(cli_addr);

    while (1){
   
        bzero(&cli_addr,cli_len);
        //获取客户端连接
        int clientFD= accept(socket_fd,(struct sockaddr*)&cli_addr,&cli_len);
        if (clientFD == -1){
   
            printf("accept error\n");
            exit(EXIT_FAILURE);
        }

        printf("连接 ip:%s, port:%d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));



        //创建线程

        pthread_t tid;

        int ret= pthread_create( &tid, NULL, (void*)do_client, (void*)&clientFD );
        if (ret != 0){
   
            printf("pthread_create error\n");
            exit(EXIT_FAILURE);
        }

        pthread_detach(tid);
    }

}

客户端代码

//todo tcp多线程客户端,循环发送数据,接收回传数据,解决粘包问题,不定长的数据传输
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define N 128
//初始化socket
int  init_socket(char *ip,char *port){
   

    int init_socket_fd= socket(AF_INET,SOCK_STREAM,0);
    if (init_socket_fd==-1){
   
        printf("init_socket err");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in server_addr;
    socklen_t len=sizeof(server_addr);
    bzero(&server_addr,len);
    server_addr.sin_family=AF_INET;
    inet_aton(ip,&server_addr.sin_addr);
    server_addr.sin_port= htons(atoi(port));

    //连接
    int ret= connect(init_socket_fd,(struct sockaddr*)&server_addr,len);
    if (ret==-1){
   
        printf("connect error,连接失败\n");
        exit(EXIT_FAILURE);
    }


    return init_socket_fd;
}

//客户端接收数据
int Client_Receive_data(int socket_fd){
   
    char receive_msg[N];
    bzero(receive_msg,N);
    int recv_len= recv(socket_fd, receive_msg,sizeof(receive_msg),0);
    if (recv_len == -1) {
   
        printf("recv error\n");
        exit(EXIT_FAILURE);
    }

    receive_msg[recv_len] = '\0';
    printf("收到客户端数据:[%s]\n",receive_msg);
}


//客户端发送数据
int  Client_Send_data(int socket_fd){
   
    char msg[N];

    while (1){
   
        bzero(&msg, sizeof (msg));
        printf("请输入:\n");
        fgets(msg, sizeof(msg),stdin);
        msg[strlen(msg)-1]='\0';

        printf("发送数据%s\n",msg);

        //粘包解决
        int old_msg_length= strlen(msg);
        char *New_msg=(char *) malloc(old_msg_length+4);
        memcpy(New_msg,&old_msg_length,4);
        memcpy(New_msg+4,msg,old_msg_length);


        int  Send_data_len= send(socket_fd,New_msg, old_msg_length+4,0);
        if (Send_data_len==-1){
   
            printf("发送失败 send err\n");
            exit(EXIT_FAILURE);
        }
        printf("发送了%d个字节\n",Send_data_len);

        free(New_msg);

        if (strncmp(msg, "exit", 4) == 0) {
   
            printf("退出通信\n");
            close(socket_fd);
            break;
        }
        //接收
        //Client_Receive_data(socket_fd);

    }

    return 0;
}

int main(){
   
    //初始化连接
    int socket_fd = init_socket("172.17.140.183","8080");
    //发送数据
    Client_Send_data(socket_fd);


    return 0;
}
相关文章
|
负载均衡 算法 安全
基于Reactor模式的高性能网络库之线程池组件设计篇
EventLoopThreadPool 是 Reactor 模式中实现“一个主线程 + 多个工作线程”的关键组件,用于高效管理多个 EventLoop 并在多核 CPU 上分担高并发 I/O 压力。通过封装 Thread 类和 EventLoopThread,实现线程创建、管理和事件循环的调度,形成线程池结构。每个 EventLoopThread 管理一个子线程与对应的 EventLoop(subloop),主线程(base loop)通过负载均衡算法将任务派发至各 subloop,从而提升系统性能与并发处理能力。
597 3
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
439 1
|
10月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
407 1
|
调度 Python
探索Python高级并发与网络编程技术。
可以看出,Python的高级并发和网络编程极具挑战,却也饱含乐趣。探索这些技术,你将会发现:它们好比是Python世界的海洋,有穿越风暴的波涛,也有寂静深海的奇妙。开始旅途,探索无尽可能吧!
347 15
|
网络协议 物联网
VB6网络通信软件上位机开发,TCP网络通信,读写数据并处理,完整源码下载
本文介绍使用VB6开发网络通信上位机客户端程序,涵盖Winsock控件的引入与使用,包括连接服务端、发送数据(如通过`Winsock1.SendData`方法)及接收数据(利用`Winsock1_DataArrival`事件)。代码实现TCP网络通信,可读写并处理16进制数据,适用于自动化和工业控制领域。提供完整源码下载,适合学习VB6网络程序开发。 下载链接:[完整源码](http://xzios.cn:86/WJGL/DownLoadDetial?Id=20)
600 12
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
707 23
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
616 20
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
726 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
网络协议 测试技术 Linux
Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 是一个基于 epoll 和 kqueue 实现的高性能事件循环库,适用于 Linux 和 macOS(Windows 暂不支持)。它支持多核多线程、动态扩容的 Ring Buffer 读写缓冲区、异步读写和 SO_REUSEPORT 端口重用。gev 使用少量 goroutine,监听连接并处理读写事件。性能测试显示其在不同配置下表现优异。安装命令:`go get -u github.com/Allenxuxu/gev`。
460 0