天天动画片 > 八卦谈 > Linux多线程服务器设计

Linux多线程服务器设计

八卦谈 佚名 2024-02-03 08:26:31

1.socket网络编程流程简述

有关于Linux socket网络编程的具体细则笔者在这先不展开,整个通信简单来说就是基于socket套接字可以实现两台设备之间的通信,其中一台设备需要将其网络的IP协议族、IP地址和端口号绑定到一个创建好的socket上,然后监听该socket等待设备连接,这台设备就是大家所认知的服务器,另外一台设备通过服务器的同样需要将服务器的IP协议族、IP地址和端口号绑定到一个socket,然后基于该socket可以实现和服务器的连接,以上这种方式socket网络编程的大致流程如下:

2.基于socket服务器和客户端通信流程

服务器

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

#define SERVER_PORT 9999
#define SERVER_IP   "169.254.188.233"

#define BUFSIZE 1024

int main(void)
{   
    int sockfd;
    int client_fd;
    int ret;
    int addrlen;
    struct sockaddr_in socket_info;
    struct sockaddr_in client_info;
    char buf[BUFSIZE];

    //创建socket套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("socket");
        exit(0);
    }

    //绑定服务器信息
    memset(&socket_info, 0sizeof(socket_info));
    socket_info.sin_family = AF_INET;   //ipv4
    socket_info.sin_port = htons(SERVER_PORT);  //端口号
    socket_info.sin_addr.s_addr = inet_addr(SERVER_IP); //ip地址

    ret = bind(sockfd, (struct sockaddr *)&socket_info, sizeof(socket_info));
    if(ret < 0){
        perror("bind");
        exit(0);
    }

    //设置监听套接字
    ret = listen(sockfd, 10);
    if(ret < 0){
        perror("listen");
        exit(0);
    }

    printf("server waiting client……\r\n");

    //等待客户端连接
    client_fd = accept(sockfd, (struct sockaddr *)&client_info, &addrlen);
    if(client_fd < 0){
        perror("accept");
        exit(0);
    }else{
        printf("client information:\r\n");
        printf("client socket fd = %d\r\n", client_fd);
        printf("client inet:%s \r\n", inet_ntoa(client_info.sin_addr));
        printf("client port = %d\r\n", ntohs(client_info.sin_port));
    }

    //和客户端通信
    while(1){
        ret = recv(client_fd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("recv");
        }else if(ret == 0){
            printf("client shutdown!\r\n");
            exit(0);
        }else{
            printf("C->S:%s\r\n", buf);
        }

        ret = send(client_fd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("send");
        }
    }
    close(client_fd);
    close(sockfd);
}

客户端

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

#define SERVER_PORT 9999
#define SERVER_IP   "169.254.188.233"

#define BUFSIZE 1024

int main(void)
{
    int sockfd;
    int client_fd;
    int ret;
    struct sockaddr_in server_info;
    struct sockaddr_in client_info;
    char buf[BUFSIZE];

    //创建socket套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("socket");
        exit(0);
    }

    //绑定服务器信息
    memset(&server_info, 0sizeof(server_info));
    server_info.sin_family = AF_INET;   //ipv4
    server_info.sin_port = htons(SERVER_PORT);  //端口号
    server_info.sin_addr.s_addr = inet_addr(SERVER_IP); //ip地址

    //连接服务器
    ret = connect(sockfd, (struct sockaddr *)&server_info, sizeof(server_info));
    if(ret < 0){
        perror("connet");
        exit(0);
    }

    //和客户端通信
    while(1){
        printf("C->S:please input data:\r\n");
        read(0, buf, sizeof(buf));
        ret = send(sockfd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("send");
        }

        ret = recv(sockfd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("recv");
        }else if(ret == 0){
            printf("server shutdown!\r\n");
            exit(0);
        }else{
            printf("S->C:%s\r\n", buf);
        }

        memset(buf, 0, BUFSIZE);
    }

    close(sockfd);
}

3.为什么需要多线程服务器

那么正常使用情况都是一个服务器对应有多个客户端的,举个最简单的例子:我们登陆的微信号就是一个客户端,通过网络接入腾讯的服务器,那么腾讯的这个微信的服务器就是唯一的,但是我们的微信号可以会对应很多用户。类似于上述情况那么由前面的一个服务器对应一个客户端来通信的情况在实际应用中是不可能存在的,那么一个服务器就需要同时能够处理多个客户端的请求,在Linux中实现这种通信最常见的方式有多进程和多线程,相比于多进程的来说,多线程具有轻量、任务切换快等优点
多线程服务器使用的核心工作在于,等待客户端连接的工作必须始终能够运行,同时已连接客户端的数据收发功能也需要正常进行,所以,解决的方案就是每连接上一个客户端就开一个线程,在该线程中处理客户端的事务,多线程服务器的代码实现如下:

多线程并发服务器的实现:

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

#define SERVER_PORT 9999
#define SERVER_IP   "169.254.188.233"

#define BUFSIZE 1024

typedef struct{
    int s_fd;
    struct sockaddr_in s_info;
}client_info_in; 

void *thread_client_manage(void *arg);

int main(void)
{   
    int sockfd;
    int client_fd;
    pthread_t tid;
    int ret;
    int addrlen;
    struct sockaddr_in socket_info;
    client_info_in client_info_t;
    char buf[BUFSIZE];

    //创建socket套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("socket");
        exit(0);
    }

    //绑定服务器信息
    memset(&socket_info, 0sizeof(socket_info));
    socket_info.sin_family = AF_INET;   //ipv4
    socket_info.sin_port = htons(SERVER_PORT);  //端口号
    // socket_info.sin_addr.s_addr = inet_addr(SERVER_IP); //ip地址
    socket_info.sin_addr.s_addr = INADDR_ANY;    //0.0.0.0 -- 默认找到你能上网的网卡 直接绑定对应的IP

    //解决服务重连地址拒绝问题  
    int reuse=1;
    ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //本地地址可重复使用

    ret = bind(sockfd, (struct sockaddr *)&socket_info, sizeof(socket_info));
    if(ret < 0){
        perror("bind");
        exit(0);
    }

    //设置监听套接字
    ret = listen(sockfd, 10);
    if(ret < 0){
        perror("listen");
        exit(0);
    }

    //和客户端通信
    while(1){
        printf("server waiting client……\r\n");
        //等待客户端连接
        client_info_t.s_fd = accept(sockfd, (struct sockaddr *)&client_info_t.s_info, &addrlen);
        if(client_fd < 0){
            perror("accept");
            exit(0);
        }else{
            printf("client information:\r\n");
            printf("client socket fd = %d\r\n", client_fd);
            printf("client inet:%s \r\n", inet_ntoa(client_info_t.s_info.sin_addr));
            printf("client port = %d\r\n", ntohs(client_info_t.s_info.sin_port));
        }

        //客户端已连接  创建一个线程 --- 处理此时连接的这个客户端的信息
        pthread_create(&tid, NULL, thread_client_manage, (void *)&client_info_t);
    }
    close(client_fd);
    close(sockfd);
}

void *thread_client_manage(void *arg)
{
    int ret;
    char buf[BUFSIZE];
    client_info_in info = *(client_info_in *)arg;   //结构体变量整体赋值

    pthread_detach(pthread_self());         //将本线程分离 -- 在线程结束直接有系统回收空间

    while(1){
        memset(buf, 0, BUFSIZE);
        //处理客户端事物
        ret = recv(info.s_fd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("recv");
        }else if(ret == 0){
            printf("client shutdown!\r\n");
            pthread_exit(NULL); //客户端断开后,销毁本线程
        }else{
            printf("C->S:%d:%s\r\n", info.s_fd, buf);
        }

        ret = send(info.s_fd, buf, BUFSIZE, 0);
        if(ret < 0){
            perror("send");
        }

        if(strstr(buf, "exit") != NULL){
            printf("client exit\r\n");
            pthread_exit(NULL); //客户端断开后,销毁本线程        
        }        
    }
}

运行结果如下:


本文标题:Linux多线程服务器设计 - 八卦谈
本文地址:www.ttdhp.com/article/47081.html

天天动画片声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
扫码关注我们