介绍

C Liunx实现EchoServer非阻塞服务端(TPC协议)

支持库

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>

代码

typedef struct EchoServerInfo
{
    int socketFD;
    struct sockaddr_in serverAddr;
    int bufferSize;
    struct timeval timeout;
    bool block;
    bool serverIsOpen;

    int clientFD;
    struct sockaddr_in clientAddr;
} EchoServer;

EchoServer *InitServer();

int LoadServer(EchoServer *server, int port, bool noBlock);

void OpenServer(EchoServer *server);

void HandleServerConnect(EchoServer *server);

char *GetClientDate(EchoServer *server, bool fastRead);

char *GetClientIP(EchoServer *server);

int SendClientDate(EchoServer *server, char *date);

void SetServerTimeout(EchoServer *server, int s, int us);

void CloseServer(EchoServer *server);

void FreeServer(EchoServer *server);

EchoServer *InitServer()
{
    EchoServer *result = (EchoServer *)malloc(sizeof(EchoServer));
    if (result != NULL)
    {
        memset(result, 0, sizeof(EchoServer));
        result->socketFD = 0;
        result->clientFD = 0;
        result->bufferSize = 1024;
        result->serverIsOpen = false;
        SetServerTimeout(result, 3, 0);
    }
    return result;
}

int LoadServer(EchoServer *server, int port, bool noBlock)
{

    // 创建信箱
    server->socketFD = socket(AF_INET, SOCK_STREAM, 0);
    if (server->socketFD < 0)
    {
        return -1;
    }

    if (noBlock)
    {
        int flags = fcntl(server->socketFD, F_GETFL, 0);
        fcntl(server->socketFD, F_SETFL, flags | O_NONBLOCK);
    }

    server->block = !noBlock;

    server->serverAddr.sin_family = AF_INET;
    server->serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    server->serverAddr.sin_port = htons(port);

    if (bind(server->socketFD, (struct sockaddr *)&server->serverAddr, sizeof(server->serverAddr)) < 0)
        return -2;

    if (listen(server->socketFD, port) < 0)
        return -3;
}

void OpenServer(EchoServer *server)
{
    socklen_t clientAddrLen;
    clock_t start, end;
    bool block = server->block;

    server->serverIsOpen = true;
    while (server->serverIsOpen)
    {
        if (block)
            start = clock();

        clientAddrLen = sizeof(server->clientAddr);
        server->clientFD = accept(server->socketFD, (struct sockaddr *)&server->clientAddr, &clientAddrLen);

        if (block)
            end = clock();

        if (server->clientFD > 0)
        {
            HandleServerConnect(server);
            close(server->clientFD);
            continue;
        }
        if (block)
            usleep((end - start) / CLOCKS_PER_SEC);
    }
}

int SendClientDate(EchoServer *server, char *date)
{
    struct timeval timeout = server->timeout;
    fd_set wait;
    FD_ZERO(&wait);
    FD_SET(server->clientFD, &wait);

    if (select(server->clientFD + 1, NULL, &wait, NULL, &timeout) > 0)
        return write(server->clientFD, date, strlen(date));
    else
        return -1;
}

char *GetClientDate(EchoServer *server, bool fastRead)
{
    int len, ready, size = 1;
    char *temp;
    char *result = (char *)malloc(sizeof(char));
    char *buffer = (char *)malloc(sizeof(char) * server->bufferSize);
    if (!result)
        return "";
    else
        result[0] = '\0';

    if (!buffer)
        return "";
    else
        buffer[0] = '\0';

    struct timeval timeout = server->timeout;
    fd_set wait;
    FD_ZERO(&wait);
    FD_SET(server->clientFD, &wait);

    while (true)
    {
        if (server->block)
            goto ReadMessage;
        ready = select(server->clientFD + 1, &wait, NULL, NULL, &timeout);
        if (ready > 0)
        {
            if (FD_ISSET(server->clientFD, &wait))
            {
            ReadMessage:
                len = read(server->clientFD, buffer, server->bufferSize - 1);
                if (len > 0)
                {
                    size += len;
                    buffer[len] = '\0';
                    temp = (char *)realloc(result, sizeof(char) * size);
                    if (!temp)
                        break;
                    strcpy(temp, result);
                    result = temp;
                    strcat(result, buffer);
                    if (server->block)
                        continue;
                    if (len < server->bufferSize - 1 && fastRead)
                        break;
                }
                else
                    break;
            }
        }
        else
            break;
    }
    free(buffer);
    return result;
}

char *GetClientIP(EchoServer *server)
{
    char *clientIP = (char *)malloc(INET_ADDRSTRLEN);
    if (clientIP == NULL)
        return NULL;

    struct sockaddr_in *pV4Addr = &server->clientAddr;

    if (inet_ntop(AF_INET, &(pV4Addr->sin_addr), clientIP, INET_ADDRSTRLEN) == NULL)
    {
        free(clientIP);
        return NULL;
    }

    return clientIP;
}

void SetServerTimeout(EchoServer *server, int s, int us)
{
    server->timeout.tv_sec = s;
    server->timeout.tv_usec = us;
}

void CloseServer(EchoServer *server)
{
    if (server->socketFD)
        close(server->socketFD);
    server->serverIsOpen = false;
}

void FreeServer(EchoServer *server)
{
    free(server);
}

这里的void HandleServerConnect(EchoServer *server)只有申明没有定义,每当有连接建立时自动调用此函数

示例

#include "EchoServer.h" //这里别忘记改掉
#include <stdio.h>
 
void HandleServerConnect(EchoServer *server)
{
    char *ip = GetClientIP(server);
    char *date = GetClientDate(server, true);
    int len;
    printf("%s 来自IP:%s \n", date, ip);
 
    len = SendClientDate(server, "close");
 
    //CloseServer(server);
}
 
int main()
{
    EchoServer *server = InitServer();
    if (LoadServer(server, 1001, false) == 0)
    {
        OpenServer(server);
    }
    FreeServer(server);
}