Socket 基础编程(二)

在上一篇博客中,我们总结了基本的Socket C/S结构的用法。但该实例仅仅限于1vs1的C/S交互中,当我们需要处理多对一的交互时,服务器就必须支持并发处理。

我们知道在服务器accept函数收到建联请求后,会反馈一个新的fd用于与客户端交互,此时通常我们就可以新起线程专门处理与该客户端的交互,而主线程则返回继续进行accept阻塞监听。

但这样的代价是明显的,线程的损耗不光是资源占用的问题,还涉及到CPU的线程处理切换,所以更恰当的做法应是基于单线程的方法,实现并发处理。

int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。

select函数,是我们常用的非阻塞态函数,它会持续监测观察列表中的readfds, writefds 与 exceptfds,当其中的fd状态改变时,select就会返回。这就避免了socket函数中的accept监听、recv等的阻塞状态。

新的实现对原有代码进行了改善,首先是服务器端采用select的方式,监听所有的建联socket的fd与服务器端bind的fd的可读状态,当存在可读时,则对所有fd的状态进行遍历和处理。
客户端侧,利用fork函数生成多个子进程,根据进程号决定间隔时长,从而模拟多用户的多次交互行为。

Server:

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

#define SUCCESS 0
#define SERVERPORT 8888
#define SOCKMAXCONN 1024

struct fd_node
{
    int fd;
    struct fd_node *next;
};

struct fd_node *free_list = NULL;
struct fd_node *work_list = NULL;

int init_node_list()
{
    struct fd_node *curr = NULL;
    struct fd_node *temp = NULL;

    free_list = (struct fd_node*)malloc(sizeof(struct fd_node));
    free_list->fd = -1;
    free_list->next = NULL;

    work_list = (struct fd_node*)malloc(sizeof(struct fd_node));
    work_list->fd = -1;
    work_list->next = NULL;

    curr = free_list;

    for (int i=0; i<SOCKMAXCONN-1; i++)
    {
        temp = (struct fd_node*)malloc(sizeof(struct fd_node));

        temp->fd = -1;
        temp->next = NULL;

        curr->next = temp;
        curr = temp;
    }

    return SUCCESS;
}

int free_node_list()
{
    struct fd_node *curr = NULL;
    struct fd_node *temp = NULL;

    curr = free_list;
    while (curr)
    {
        temp = curr;
        curr = curr->next;

        free(temp);
    }

    curr = work_list;
    while (curr)
    {
        temp = curr;
        curr = curr->next;

        free(temp);
    }
}

int main()
{   
    char buffer[1024] = {0};

    fd_set readfds;
    int client_conn = 0;

    struct sockaddr_in client_socket_addr;
    struct sockaddr_in server_socket_addr;

    socklen_t length = sizeof(client_socket_addr);

    //////////////////////////////////////////////////////////////////////////////
    //  struct sockaddr_in {
    //      __kernel_sa_family_t    sin_family; /* Address family */
    //      __be16      sin_port;   /* Port number */
    //      struct in_addr  sin_addr;   /* Internet address */
    //      unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
    //  };
    ///////////////////////////////////////////////////////////////////////////////

    server_socket_addr.sin_family = AF_INET;
    server_socket_addr.sin_port = htons(SERVERPORT);
    server_socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //////////////////////////////////////////////////////////////////////////////
    //  Create a new socket of type TYPE in domain DOMAIN, using
    //  protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
    //  Returns a file descriptor for the new socket, or -1 for errors.
    //  extern int socket (int __domain, int __type, int __protocol) __THROW;
    ///////////////////////////////////////////////////////////////////////////////

    int server_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    ///////////////////////////////////////////////////////////////////////////////
    //  Give the socket FD the local address ADDR (which is LEN bytes long). 
    //  extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
    ///////////////////////////////////////////////////////////////////////////////

    if (SUCCESS != bind(server_socket_fd, (struct sockaddr *)&server_socket_addr, sizeof(server_socket_addr)))
    {
        perror ("Bind Socket Failed: ");
        goto exit;
    }

    ///////////////////////////////////////////////////////////////////////////////
    //  Prepare to accept connections on socket FD.
    //  N connection requests will be queued before further requests are refused.
    //  Returns 0 on success, -1 for errors.
    //  extern int listen (int __fd, int __n) __THROW;
    ///////////////////////////////////////////////////////////////////////////////

    if(SUCCESS != listen(server_socket_fd, SOCKMAXCONN))
    {
        perror ("Listen Socket Failed: ");
        goto exit;
    }

#ifdef NOBLOCK

    init_node_list();

    for (;;)
    {
        int max_fd = server_socket_fd;
        struct fd_node *curr = work_list; 

        FD_ZERO (&readfds);
        FD_SET (server_socket_fd, &readfds);

        int i = 0;
        while (curr->next)
        {
            max_fd = server_socket_fd>curr->fd ? server_socket_fd : curr->fd;
            FD_SET (curr->fd, &readfds);
            curr = curr->next;
            ++i;
        }
        printf ("Monitor number is : %d \n", i);

        if (select(max_fd+1, &readfds, NULL, NULL, NULL)<0)
        {
           perror("Select Failed: ");
        }

        if (FD_ISSET(server_socket_fd, &readfds))
        {
            FD_CLR(server_socket_fd, &readfds);
            curr = free_list;
            if (NULL == curr)
            {
                printf("Reach MAX FD number. \n");
                continue;
            }

            curr->fd = accept(server_socket_fd, (struct sockaddr*)&client_socket_addr, &length);
            free_list = curr->next;
            curr->next = work_list;
            work_list = curr;

            printf ("Add a new fd: %d \n", curr->fd);
        }

        curr = work_list;
        while (curr->next)
        {
            if (FD_ISSET(curr->fd, &readfds))
            {
                memset(buffer, 0, sizeof(buffer));
                int client_len = recv(curr->fd, buffer, sizeof(buffer), 0);
                if (-1 == client_len)
                {
                    perror ("Recv Socket Failed: ");
                }   

                if (0 == strncmp(buffer, "BYE", 4))
                {
                    printf ("Del fd: %d \n", curr->fd);

                    struct fd_node *temp = work_list;
                    if (temp == curr)
                    {
                        work_list = temp->next;
                    }
                    else
                    {
                        while (temp->next != curr)
                        {
                            temp = temp->next;
                        }
                        temp->next = curr->next;    
                    }

                    curr->next = free_list;
                    free_list = curr;

                    FD_CLR(curr->fd, &readfds);
                    close(curr->fd);

                    continue;
                }

                if (-1 == send(curr->fd, buffer, client_len, 0))
                {
                    perror ("Send Socket Failed: ");
                } 
            }

            curr = curr->next;
        }
    }

    free_node_list();
#else
    for (;;)
    {
        client_conn = accept(server_socket_fd, (struct sockaddr*)&client_socket_addr, &length);
        if (-1 == client_conn)
        {
            perror ("Connect Socket Failed: ");
            goto exit;
        }

        int client_len = recv(client_conn, buffer, sizeof(buffer), 0);
        if (-1 == client_len)
        {
            perror ("Recv Socket Failed: ");
            goto exit;
        }

        if (-1 == send(client_conn, buffer, client_len, 0))
        {
            perror ("Send Socket Failed: ");
            goto exit;
        }

        printf("Received Request: %s !\n", buffer);
        close(client_conn);
    }
#endif  

    goto exit;

exit:

    close(client_conn);
    close(server_socket_fd); 

    return SUCCESS;   
}

Client:

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

#define SUCCESS 0
#define SERVERPORT 8888

int main()
{   
    int fork_times = 0;
    int random_times = 0;
    char buffer[1024] = {0};
    char *send_msg = "Hello World!";

    struct sockaddr_in server_socket_addr = {0};
    socklen_t server_addr_length = sizeof(server_socket_addr);

    server_socket_addr.sin_family = AF_INET;
    server_socket_addr.sin_port = htons(SERVERPORT);
    if( inet_pton(AF_INET, "127.0.0.1", &server_socket_addr.sin_addr) <= 0)
    {
        perror ("Inet_pton Socket Failed: ");
        goto exit;
    }

    do{
        pid_t process = fork();

        if (0 == process)
        {
            random_times = getpid()%5;
            break;
        }
        else
        {
            printf ("%d create process %d \n", getpid(), process);
            usleep(10);
        }
    } while (++fork_times < 1026);

    printf ("%d Create sub Process random: %d \n", getpid(), random_times);

    int client_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(SUCCESS != connect (client_socket_fd, (struct sockaddr*)&server_socket_addr, server_addr_length))
    {
        perror ("Connect Socket Failed: ");
        goto exit;
    }

    for (int i=0; i<random_times; i++)
    {
        if (-1 == send(client_socket_fd, send_msg, strlen("Hello World!"), 0))
        {
            perror ("Send Socket Failed: ");
            goto exit;
        }

        int client_len = recv(client_socket_fd, buffer, sizeof(buffer), 0);
        if (-1 == client_len)
        {
            perror ("Recv Socket Failed: ");
            goto exit;
        }

        sleep(random_times);
        printf("[%4d:%4d] Received Response: %s !\n", getpid(), i, buffer);
    }

    send(client_socket_fd, "BYE", strlen("BYE"), 0);

    goto exit;

exit:

    close(client_socket_fd);  
    return SUCCESS;  
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值