C/S客户端核服务端-并发服务器

使用方法

1、新建两个程序,分别引用两个函数,先执行server端的程序,再执行client端的程序
2、实现功能:当client和sever连接成功后,从client输入什么都会传输给server端,当输入第一个字母为q时 两端程序都会退出
3、特别注意:需要修改SERVER_HOST 为自己主机地址
4、本程序编写的环境,如果时windows下执行可能需要修改头文件什么的,耐心一点看就好

gcc -v
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1)

cmake -version
cmake version 3.22.1

make -v
GNU Make 4.3

一、使用线程实现并发服务器

1、服务器端代码

优化功能:

1、再任意主机上可以运行代码
2、添加读取客户信息
3、允许地址快速重用,当服务器断开后可以快速重用

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32

void *cli_data_handle(void *arg)
{
    int newfd = *(int *)arg;
    char buf[SERVER_SIZE];
    printf("%s %d newfd = %d\n", __func__, __LINE__, newfd);
    while (1)
    {
        memset(buf, 0, SERVER_SIZE);
        int recread = read(newfd, buf, SERVER_SIZE - 1);
        if (recread > 0)
        {
            printf("%s %d buf = %s\n", __func__, __LINE__, buf);
            if (!strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT)))
            {
                printf("%s %d q======\n", __func__, __LINE__);
                break;
            }
        }

        usleep(100);
    }
    close(newfd);

    return NULL;
}
int test_server()
{
    printf("%s %d \n", __func__, __LINE__);

    int fd = -1;
    struct sockaddr_in addr_in;
    char buf[SERVER_SIZE];
    /*
    AF_INET:IPV4
    SOCK_STREAM:TCP
    */
    // 1、创建socket 得到fd
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    // 优化3 允许地址快速重用,当服务器断开后可以快速重用
    int b_reuse = 1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));

    //  2、绑定
    memset(&addr_in, 0, sizeof(addr_in));  // 将变量addr_in置0
    addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    // int rec = inet_pton(AF_INET,SERVER_HOST,(void*)&addr_in.sin_addr.s_addr);
    //    if(rec!= 1){
    //     printf("%s %d rec!= 1\n", __func__, __LINE__);
    //         return 0;
    //    }

    // addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节
    // 优化1:
    addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
    int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (rec < 0)
    {
        printf("%s %d rec<0\n", __func__, __LINE__);
        return 0;
    }

    // 3、listen() 将主动套接字转换为被动套接字
    int reclisten = listen(fd, SERVER_BACKLOG); // 允许正在进行连接的客户端数目
    if (reclisten < 0)
    {
        printf("%s %d reclisten<0\n", __func__, __LINE__);
        return 0;
    }
    // 4、阻塞等待客户端连接请求

#if 0
#if 0
    int newfd = accept(fd, NULL, NULL);
    if (newfd < 0)
    {
        printf("%s %d newfd<0\n", __func__, __LINE__);
        return 0;
    }
#else
    // 优化2:
    struct sockaddr_in addr_c;
    socklen_t addr_len = sizeof(addr_c);
    int newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
    if (newfd < 0)
    {
        printf("%s %d newfd<0\n", __func__, __LINE__);
        return 0;
    }

    char ipV4_addr[16];
    if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
    {
        // 为空取反 不为空 就执行这里
        printf("%s %d newfd<0\n", __func__, __LINE__);
        return 0;
    }

    printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
    printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
#endif
#else
    pthread_t tid;
    struct sockaddr_in addr_c;
    socklen_t addr_len = sizeof(addr_c);
    int newfd;
    while (1)
    {

        newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
        if (newfd < 0)
        {
            printf("%s %d newfd<0\n", __func__, __LINE__);
            return 0;
        }

        char ipV4_addr[16];
        if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
        {
            // 为空取反 不为空 就执行这里
            printf("%s %d newfd<0\n", __func__, __LINE__);
            return 0;
        }
        printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
        printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
        pthread_create(&tid, NULL, cli_data_handle, (void *)&newfd);
    }

    close(fd);
    printf("%s %d xxxxxxxxxxxxxx newfd = %d\n", __func__, __LINE__, fd);

#endif

    // 5、读写
    // 和最新的newfd进行通信

    return 0;
}

2、客户端代码

注意:

使用该函数时需要注意输入参数:
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服务器主机的IP和是设置的端口号

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32

void usage(char *s)
{
    printf("%s %d %s serv_ip ser_port\n", __func__, __LINE__, s);
    printf("%s %d  serv_ip : server ip address\n", __func__, __LINE__);
    printf("%s %d  ser_port: server port (>5000)\n", __func__, __LINE__);
}
/*
./client serv_ip ser_port

*/
int test_client(int argc, char **argv)
{
    printf("%s %d \n", __func__, __LINE__);
    int port;
    if (argc != 3)
    {
        usage(argv[0]);
        return 0;
    }

    int fd = -1;
    struct sockaddr_in addr_in;
    char buf[SERVER_SIZE];
    /*
    AF_INET:IPV4
    SOCK_STREAM:TCP
    */
    // 1、创建socket 得到fd
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    port = atoi(argv[2]);
    if (port < 5000)
    {
        usage(argv[0]);
        return 0;
    }
    /*
    2、连接
    */
    memset(&addr_in, 0, sizeof(addr_in)); // 将变量addr_in置0
    addr_in.sin_port = htons(port);       // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    int rec = inet_pton(AF_INET, argv[1], (void *)&addr_in.sin_addr.s_addr);
    if (rec != 1)
    {
        printf("%s %d rec!= 1\n", __func__, __LINE__);
        return 0;
    }

    int reccon = connect(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (reccon < 0)
    {
        printf("%s %d reccon<0\n", __func__, __LINE__);
        return 0;
    }

    // 读写

    while (1)
    {
        memset(buf, 0, SERVER_SIZE);
        char *rec_p = fgets(buf, SERVER_SIZE - 1, stdin);

        int recwrite = write(fd, buf, strlen(buf));
        if (recwrite > 0)
        {
            printf("%s %d buf = %s\n", __func__, __LINE__, buf);
            // int recstr = strcmp(buf, SERVER_QUIT);
            int recstr = strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT));
            printf("%s %d recstr = %d\n", __func__, __LINE__, recstr);
            if (0 == recstr)
            {
                printf("%s %d q======\n", __func__, __LINE__);
                break;
            }
        }

        usleep(100);
    }
    close(fd);
    printf("%s %d xxxxxxxxxxxxxx\n", __func__, __LINE__);
    return 0;
}

下面的代码是上面的基础将进行进行修改的,可以参考着看!

二、使用进程实现并发服务器

1、服务器端代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32

void *cli_data_handle(void *arg)
{
    int newfd = *(int *)arg;
    char buf[SERVER_SIZE];
    printf("%s %d newfd = %d\n", __func__, __LINE__, newfd);
    while (1)
    {
        memset(buf, 0, SERVER_SIZE);
        int recread = read(newfd, buf, SERVER_SIZE - 1);
        if (recread > 0)
        {
            printf("%s %d buf = %s\n", __func__, __LINE__, buf);
            if (!strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT)))
            {
                printf("%s %d q======\n", __func__, __LINE__);
                break;
            }
        }

        usleep(100);
    }
    close(newfd);

    return NULL;
}

void sig_child_handle(int signo)
{
    if (signo == SIGCHLD)
    {
        waitpid(-1, NULL, WNOHANG);
    }
}
int test_server()
{
    printf("%s %d \n", __func__, __LINE__);

    int fd = -1;
    struct sockaddr_in addr_in;
    char buf[SERVER_SIZE];
    signal(SIGCHLD, sig_child_handle);
    /*
    AF_INET:IPV4
    SOCK_STREAM:TCP
    */
    // 1、创建socket 得到fd
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    // 优化4 允许地址快速重用,当服务器断开后可以快速重用
    int b_reuse = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

    //  2、绑定
    memset(&addr_in, 0, sizeof(addr_in));  // 将变量addr_in置0
    addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    // int rec = inet_pton(AF_INET,SERVER_HOST,(void*)&addr_in.sin_addr.s_addr);
    //    if(rec!= 1){
    //     printf("%s %d rec!= 1\n", __func__, __LINE__);
    //         return 0;
    //    }

    // addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节
    // 优化1:
    addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
    int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (rec < 0)
    {
        printf("%s %d rec<0\n", __func__, __LINE__);
        return 0;
    }

    // 3、listen() 将主动套接字转换为被动套接字
    int reclisten = listen(fd, SERVER_BACKLOG); // 允许正在进行连接的客户端数目
    if (reclisten < 0)
    {
        printf("%s %d reclisten<0\n", __func__, __LINE__);
        return 0;
    }
    // 4、阻塞等待客户端连接请求

    struct sockaddr_in addr_c;
    socklen_t addr_len = sizeof(addr_c);
    int newfd;

    while (1)
    {
        pid_t pid = -1;
        newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
        if (newfd < 0)
        {
            printf("%s %d newfd<0\n", __func__, __LINE__);
            break;
        }
        // 创建一个子进程用于处理已建立连接的客户的交互数据
        if ((pid = fork()) < 0)
        {
            printf("%s %d pid = fork() < 0 \n", __func__, __LINE__);
            break;
        }
        if (0 == pid)
        { // 子进程
            close(fd);
            char ipV4_addr[16];
            if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
            {
                // 为空取反 不为空 就执行这里
                printf("%s %d newfd<0\n", __func__, __LINE__);
                return 0;
            }
            printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
            printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
            cli_data_handle(&newfd);
            return 0;
        }
        else
        { // 父线程pid > 0
            close(newfd);
        }
    }

    close(fd);
    printf("%s %d xxxxxxxxxxxxxx newfd = %d\n", __func__, __LINE__, fd);

    // 5、读写
    // 和最新的newfd进行通信

    return 0;
}

2、客户端代码

和上面一致,这里不赘述。

注意:

使用该函数时需要注意输入参数:
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服务器主机的IP和是设置的端口号

你可能感兴趣的:(服务器,c语言,ubuntu)