TCP通信框架:服务端设计

问题

如何设计与客户端相对应的服务端?

TCP 通信框架设计

服务端

负责监听连接状态

  • Connect:产生通信客户端 (TcpClient),并给出事件通知
  • Close:给出事件通知,并销毁通信客户端

负责监听数据通信状态,并给出事件通知

服务端事件设计

EVT_CONN

  • 客户端连接服务端时触发,并创建 TcpClient 用于通信

EVT_DATA

  • 客户端数据到达服务端时触发,使用 TcpClient 读取数据

EVT_CLOSE

  • 客户端断开服务端时触发,相关 TcpClient 将被销毁

TCP 通信框架设计

问题:服务端如何知道什么时候进行事件回调通知?

服务端通过 select 机制循环触发事件回调!

服务端接口设计 

服务端设计与实现

tcp_server.h

#ifndef TCP_SERVER_H
#define TCP_SERVER_H

#include "tcp_client.h"

typedef void  TcpServer;
typedef void  (*Listener)(TcpClient*, int);

enum
{
    EVT_COON,
    EVT_DATA,
    EVT_CLOSE
};

TcpServer* TcpServer_New();
int TcpServer_Start(TcpServer* server, int port, int max);
void TcpServer_Stop(TcpServer* server);
void TcpServer_SetListener(TcpServer* server, Listener listener);
int TcpServer_IsValid(TcpServer* server);
void TcpServer_DoWork(TcpServer* server);
void TcpServer_Del(TcpServer* server);

#endif

 tcp_server.c

#include "tcp_server.h"
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <malloc.h>
#include <sys/types.h>
#include <arpa/inet.h>

#define FD_SIZE  1024

typedef struct tcp_server
{
    int fd;
    int valid;
    Listener cb;
    TcpClient* client[FD_SIZE];
} Server;

TcpServer* TcpServer_New()
{
    Server* ret = malloc(sizeof(Server));

    if(ret)
    {
        ret->fd = -1;
        ret->valid = 0;
        ret->cb = NULL;

        for(int i = 0; i < FD_SIZE; i++)
        {
            ret->client[i] = NULL;
        }
    }

    return ret;
}

int TcpServer_Start(TcpServer* server, int port, int max)
{
    int ret = 0;
    Server* s = (Server*)server;

    if(s && !s->valid)
    {
        struct sockaddr_in addr = {0};

        s->fd = socket(PF_INET, SOCK_STREAM, 0);

        s->valid = (s->fd != -1);

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(port);

        s->valid = (s->valid && (bind(s->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1));
        s->valid = (s->valid && (listen(s->fd, max) != -1));

        ret = s->valid;
    }

    return ret;
}

void TcpServer_Stop(TcpServer* server)
{
    Server* s = (Server*)server;

    if(s)
    {
        s->fd = -1;
        s->valid = 0;
        s->cb = NULL;

        for(int i = 0; i < FD_SIZE; i++)
        {
            TcpClient_Del(s->client[i]);

            s->client[i] = NULL;
        }
    }
}

void TcpServer_SetListener(TcpServer* server, Listener listener)
{
    Server* s = (Server*)server;

    if(s)
    {
        s->cb = listener;
    }
}

int TcpServer_IsValid(TcpServer* server)
{
    return (server) ? ((Server*)server)->valid : 0;
}

int Select_Handler(Server* s, fd_set* reads, fd_set* rset, int max)
{
    int ret = max;

    for(int i = 0; i <= max; i++)
    {
        if(FD_ISSET(i, rset))
        {
            int event = -1;
            int index = i;

            if(i == s->fd)
            {
                struct sockaddr_in caddr = {0};
                socklen_t csize = sizeof(caddr);

                index = accept(s->fd, (struct sockaddr*)&caddr, &csize);

                if(index != -1)
                {
                    FD_SET(index, reads);

                    s->client[index] = TcpClient_From(index);

                    ret = (index > max) ? index : max;

                    event = EVT_COON;
                }
            }
            else
            {
                event = EVT_DATA;
            }

            if(s->cb)
            {
                if(TcpClient_IsValid(s->client[index]))
                {
                    s->cb(s->client[index], event);
                }
                else
                {
                    if(s->client[index])
                    {
                        s->cb(s->client[index], EVT_CLOSE);
                    }

                    FD_CLR(index, reads);

                    TcpClient_Del(s->client[index]);

                    s->client[index] = NULL;
                }
            }
        }
    }

    return ret;
}

void TcpServer_DoWork(TcpServer* server)
{
    Server* s = (Server*)server;

    if(s && s->valid)
    {
        int num = 0;
        int max = s->fd;
        fd_set reads = {0};
        fd_set rset = {0};
        struct timeval timeout = {0};

        FD_ZERO(&reads);
        FD_SET(s->fd, &reads);

        while(s->valid)
        {
            rset = reads;

            timeout.tv_sec = 0;
            timeout.tv_usec = 10000;

            num = select(max + 1, &rset, NULL, NULL, &timeout);

            if(num > 0)
            {
                max = Select_Handler(s, &reads, &rset, max);
            }
        }
        
    }
}

void TcpServer_Del(TcpServer* server)
{
    TcpServer_Stop(server);
    free(server);
}

服务端在收到客户端的连接请求、数据或断开请求后,会产生相应的事件,触发回调函数,我们可以根据事件的类型,来处理不同的事件。

当客户端断开连接时,服务端中与之通信的 socket 中的可读事件会发生,select 返回,产生并向与之通信的 TcpClient发送一个 EVT_CLOSE 事件。

测试代码

client.c

#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "message.h"
#include "tcp_client.h"

int main()
{
	TcpClient* client = TcpClient_New();
	char* test = "D.T.Software";

	if(client && TcpClient_Connect(client, "127.0.0.1", 8888))
	{
		printf("connect succeed\n");

		for(int i = 0; i < strlen(test); i++)
		{
			char buf[2] = {0};

			buf[0] = test[i];

			Message* pm = Message_New(128, 129, i, strlen(test), buf, sizeof(buf));

			TcpClient_SendMsg(client, pm);

			free(pm);
		}
	}

	getchar();

	TcpClient_Del(client);

	return 0;
}

server.c

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "tcp_server.h"

void EventListener(TcpClient* client, int evt)
{
	if(evt == EVT_COON)
	{
		printf("Connect: %p\n", client);
	}
	else if(evt == EVT_DATA)
	{
		Message* msg = TcpClient_RecvMsg(client);

		if(msg)
		{
			char* s = TcpClient_GetData(client);

			if(msg->index == 0)
			{
				s = (char*)malloc(msg->total + 1);

				TcpClient_SetData(client, s);
			}
			
			strcpy(s + msg->index, msg->payload);

			if((msg->index + 1) == msg->total)
			{
				printf("%s\n", s);

				free(s);
			}

			free(msg);
		}
	}
	else if(evt == EVT_CLOSE)
	{
		printf("Close: %p\n", client);
	}
}

int main()
{
	TcpServer* server = TcpServer_New();

	if(server)
	{
		int r = TcpServer_Start(server, 8888, 20);

		printf("r = %d\n", r);

		if(r)
		{
			TcpServer_SetListener(server, EventListener);

			TcpServer_DoWork(server);
		}
	}

	TcpServer_Del(server);

	return 0;
}

程序运行结果如下所示:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值