Socket TCP服务程序示例

      在socket网络编程中,许多应用场景需要用到TCP服务端,监听本地端口,处理TCP客户端连接,与多个客户端进行通信。而处理多个连接,与多个连接进行通信,还是要花点功夫的。

      为了方便日后能够快速使用tcp server开发项目,特地从工作中总结出来如下TCP Server示例程序,可供大家参考。

socket_server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <errno.h>
#include <stdint.h>
#include "socket_server.h"


/**
 * TYPEDEFS
 */
typedef struct
{
    void   *next;
    int socketFd;
    socklen_t cli_len;
    struct sockaddr_in cli_addr;
} socketRecord_t;


/**
 * GLOBAL VARIABLES
 */
socketRecord_t *socketRecordHead = NULL;

socketServerCb_t socketServerRxCb;
socketServerCb_t socketServerConnectCb;

/**
 * @fn      createSocketRec
 * @brief   create a socket and add a rec fto the list.
 * @param   table
 * @param   rmTimer
 * @return  new clint fd
 */
int createSocketRec( void  )
{
    int tr=1;
    socketRecord_t *srchRec;

    socketRecord_t *newSocket = malloc( sizeof( socketRecord_t ) );

    //open a new client connection with the listening socket (at head of list)
    newSocket->cli_len = sizeof(newSocket->cli_addr);

    //Head is always the listening socket
    newSocket->socketFd = accept(socketRecordHead->socketFd,
                                 (struct sockaddr *) &(newSocket->cli_addr),
                                 &(newSocket->cli_len));

    if (newSocket->socketFd < 0)
        printf("accept error!");
           

    // Set the socket option SO_REUSEADDR to reduce the chance of a
    // "Address Already in Use" error on the bind
    setsockopt(newSocket->socketFd,SOL_SOCKET,SO_REUSEADDR,&tr,sizeof(int));
    // Set the fd to none blocking
    fcntl(newSocket->socketFd, F_SETFL, O_NONBLOCK);

    newSocket->next = NULL;

    //find the end of the list and add the record
    srchRec = socketRecordHead;
    // Stop at the last record
    while ( srchRec->next )
        srchRec = srchRec->next;

    // Add to the list
    srchRec->next = newSocket;

    return(newSocket->socketFd);
}

/**
 * @fn      deleteSocketRec
 * @brief   Delete a rec from list.
 * @param   table
 * @param   rmTimer
 * @return  none
 */
void deleteSocketRec( int rmSocketFd )
{
    socketRecord_t *srchRec, *prevRec=NULL;

    // Head of the timer list
    srchRec = socketRecordHead;

    // Stop when rec found or at the end
    while ( (srchRec->socketFd != rmSocketFd) && (srchRec->next) )
    {
        prevRec = srchRec;
        // over to next
        srchRec = srchRec->next;
    }

    if (srchRec->socketFd != rmSocketFd)
    {
        printf("deleteSocketRec: record not found");
        return;
    }

    // Does the record exist
    if ( srchRec )
    {
        // delete the timer from the list
        if ( prevRec == NULL )
        {
            //trying to remove first rec, which is always the listining socket
            printf("deleteSocketRec: removing first rec, which is always the listining socket.");
            return;
        }

        //remove record from list
        prevRec->next = srchRec->next;

        close(srchRec->socketFd);
        free(srchRec);
    }
}

/**
 * @fn      serverSocketInit
 * @brief   initialises the server.
 * @param
 * @return  Status
 */
int32_t socketServerInit( uint32_t port )
{
    struct sockaddr_in serv_addr;
    int stat, tr=1;

    if(socketRecordHead == NULL)
    {
        // New record
        socketRecord_t *lsSocket = malloc( sizeof( socketRecord_t ) );

        lsSocket->socketFd = socket(AF_INET, SOCK_STREAM, 0);
        if (lsSocket->socketFd < 0)
        {
            printf("opening socket fail");
            return -1;
        }

        // Set the socket option SO_REUSEADDR to reduce the chance of a
        // "Address Already in Use" error on the bind
        setsockopt(lsSocket->socketFd, SOL_SOCKET, SO_REUSEADDR, &tr, sizeof(int));
        // Set the fd to none blocking
        fcntl(lsSocket->socketFd, F_SETFL, O_NONBLOCK);

        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(port);
        stat = bind(lsSocket->socketFd, (struct sockaddr *) &serv_addr,
                    sizeof(serv_addr));
        if ( stat < 0)
        {
            printf("binding error: %s\n", strerror( errno ) );
            return -1;
        }
        //will have MAX_CLIENTS pending open client requests
        listen(lsSocket->socketFd, MAX_CLIENTS);

        lsSocket->next = NULL;
        //Head is always the listening socket
        socketRecordHead = lsSocket;
    }
	
    return 0;
}

/**
 * @fn      serverSocketConfig
 * @brief   register the Rx Callback.
 * @param
 * @return  Status
 */
int32_t socketServerConfig(socketServerCb_t rxCb, socketServerCb_t connectCb)
{
    socketServerRxCb = rxCb;
    socketServerConnectCb = connectCb;

    return 0;
}
/**
 * @fn      socketSeverGetClientFds()
 * @brief   get clients fd's.
 * @param   none
 * @return  list of Timerfd's
 */
void socketServerGetClientFds(int *fds, int maxFds)
{  
    uint32_t recordCnt=0;
    socketRecord_t *srchRec;

    // Head of the timer list
    srchRec = socketRecordHead;
    
    // Stop when at the end or max is reached
    while ( (srchRec) && (recordCnt < maxFds) )
    {
        fds[recordCnt++] = srchRec->socketFd;

        srchRec = srchRec->next;
    }

    return;
}

/**
 * @fn      socketSeverGetNumClients()
 * @brief   get clients fd's.
 * @param   none
 * @return  list of Timerfd's
 */
uint32_t socketServerGetNumClients(void)
{  
    uint32_t recordCnt=0;
    socketRecord_t *srchRec;

    // Head of the timer list
    srchRec = socketRecordHead;

    if(srchRec==NULL)
    {
        printf("socketSeverGetNumClients: socketRecordHead NULL\n");
        return -1;
    }
    
    // Stop when rec found or at the end
    while ( srchRec )
    {
        srchRec = srchRec->next;
        recordCnt++;
    }

   // printf("socketSeverGetNumClients %d", recordCnt);
    return (recordCnt);
}


/**
 * @fn      socketSeverPoll()
 * @brief   services the Socket events.
 * @param   clinetFd - Fd to services
 * @param   revent - event to services
 * @return  none
 */
void socketServerPoll(int clientFd, int revent)
{
    //is this a new connection on the listening socket
    if(clientFd == socketRecordHead->socketFd)
    {
        int newClientFd = createSocketRec();

        if(socketServerConnectCb)
        {
            socketServerConnectCb(newClientFd);
        }
    }
    else
    {
        //this is a client socket is it a input or shutdown event
        if (revent & POLLIN)
        {
            //its a Rx event
            if(socketServerRxCb)
            {
                socketServerRxCb(clientFd);
            }

        }
        if (revent & POLLRDHUP)
        {
            //its a shut down close the socket
            printf("Client fd:%d disconnected\n", clientFd);

            //remove the record and close the socket
            deleteSocketRec(clientFd);
        }
    }


    //write(clientSockFd,"I got your message",18);

    return;
}

/**
 * @fn      socketSeverSend
 * @brief   Send a buffer to a clients.
 * @param   uint8* srpcMsg - message to be sent
 *          int32 fdClient - Client fd
 * @return  Status
 */
int32_t socketServerSend(uint8_t* buf, uint32_t len, int32_t fdClient)
{ 
    int32_t rtn;
    if(fdClient)
    {
        rtn = write(fdClient, buf, len);
        if (rtn < 0)
        {
            printf("write date to fdClient %d error!", fdClient);
            return rtn;
        }
    }
    return 0;
}  


/**
 * @fn      socketSeverSendAllclients
 * @brief   Send a buffer to all clients.
 * @param   uint8* srpcMsg - message to be sent
 * @return  Status
 */
int32_t socketServerSendAllclients(uint8_t* buf, uint32_t len)
{ 
    int rtn;
    socketRecord_t *srchRec;
    srchRec = socketRecordHead->next;
    
    // Stop when at the end or max is reached
    while ( srchRec )
    {
        rtn = write(srchRec->socketFd, buf, len);
        if (rtn < 0)
        {
			printf("write date to socket %d error!", srchRec->socketFd);
			printf("closing client socket");
			
            deleteSocketRec(srchRec->socketFd);
            return rtn;
        }
        srchRec = srchRec->next;
    }
    
    return 0;
}

/**
 * @fn      socketSeverClose
 * @brief   Closes the client connections.
 * @return  Status
 */
void socketServerClose(void)
{
    int fds[MAX_CLIENTS], idx=0;

    socketServerGetClientFds(fds, MAX_CLIENTS);

    while(socketServerGetNumClients() > 1)
    {		
		printf("socketSeverClose: Closing socket fd:%d\n", fds[idx]);
        deleteSocketRec( fds[idx++] );
    }
    
    //Now remove the listening socket
    if(fds[0])
    {
        printf("socketSeverClose: Closing the listening socket.");
        close(fds[0]);
    }
}

 socket_server.h

#ifndef SOCKET_SERVER_H
#define SOCKET_SERVER_H

# define POLLRDHUP	0x2000

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * TYPEDEFS
 */

typedef void (*socketServerCb_t)( int clientFd );

/**
 * INCLUDES
 */
#include <stdint.h>
//#include "hal_types.h"

/**
 * CONSTANTS
 */

#define MAX_CLIENTS 5

/*
 * serverSocketInit - initialises the server.
 */
int32_t socketServerInit( uint32_t port );

/*
 * serverSocketConfig - initialises the server.
 */
int32_t socketServerConfig(socketServerCb_t rxCb, socketServerCb_t connectCb);

/*
 * getClientFds -  get clients fd's.
 */
void socketServerGetClientFds(int *fds, int maxFds);

/* 
 * getClientFds - get clients fd's.
 */
uint32_t socketServerGetNumClients(void);

/*
 * socketSeverPoll - services the Socket events.
 */
void socketServerPoll(int clinetFd, int revent);

/*
 * socketSeverSendAllclients - Send a buffer to all clients.
 */
int32_t socketServerSendAllclients(uint8_t* buf, uint32_t len);

/*
 * socketSeverSend - Send a buffer to a clients.
 */
int32_t socketServerSend(uint8_t* buf, uint32_t len, int32_t fdClient);


/*
 * socketSeverClose - Closes the client connections.
 */
void socketServerClose(void);


#ifdef __cplusplus
}
#endif

#endif /* SOCKET_SERVER_H */

app_tcp_server.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <time.h>

#include "app_tcp_server.h"
#include "socket_server.h"

/*TCP线程ID*/
static pthread_t ulAPPTCPThreadID;
static int TCP_isrunning_flag = 0;

/*解析APP发来的JSON请求*/
void parse_app_data(int clientFd, char *request)
{
	
}

/*TCP已连接回调*/
void server_connectCB(int clientFd)
{
	printf("server_connectCB++[%d]\n", clientFd);
}


/*TCP接收数据包回调*/
void server_rxCB(int clientFd)   //change by fangye 20180806
{
  	char buffer[MAX_MESSAGE_LEN] = {0};  //单个json对象最大长度256
	int byteToRead = 0;
	int buff_index = 0;
	int flag = 0;  //记录{}标志
	int rtn = -1;
	char ch = 0;  //记录临时字符
	rtn = ioctl(clientFd, FIONREAD, &byteToRead);
	if (rtn != 0)
	{
		printf("server_rxCB: Socket error\n");
	}
	else
	{
		printf("Socket Recieve %d bytes pakage\n", byteToRead);
	}

	while (byteToRead)  //循环读取数据
	{
		ch = 0;
		read(clientFd, &ch, 1); //读一个字节
		if (ch == '{') //遇到json对象
		{
			flag++;

		}
		else if (ch == '}' && flag > 0)
		{
			flag--;
		}

		if (flag == 0) //若没有遇到json对象或者已读完一个json对象
		{
			if (buff_index > 8 && buff_index < MAX_MESSAGE_LEN-1) //判断是否有有效数据,json对象长度至少为9bytes才做处理 {"a":"b"}
			{
				buffer[buff_index++] = ch; //读取数据
				parse_app_data(clientFd, buffer); //解析数据
				printf("parse object: %s\n", buffer);
				memset(buffer, 0, buff_index);
				buff_index = 0;  //下标清零
			}
		}
		else
		{
			if (buff_index < MAX_MESSAGE_LEN-1) //json对象超出长度
				buffer[buff_index++] = ch; //读取数据
			else  //丢掉该包超过长度的数据
			{
				buff_index = 0;
				bzero(buffer, sizeof(buffer));
				while (flag == 0 || byteToRead == 0)
				{
					read(clientFd, &ch, 1); //读一个字节
					if (ch == '{') //遇到json对象
					{
						flag++;

					}
					else if (ch == '}')
					{
						flag--;
					}
					byteToRead--;
				}

			}
		}
		byteToRead--;
	}
	return;
}

/*初始化TCPServer*/
void ServerInit(void)
{
	if (socketServerInit(TCP_PORT) == -1)
	{
		printf("TCP Server init fail....................");
		return;
	}

	socketServerConfig(server_rxCB, server_connectCB);
}

/*TCPServer处理线程*/
void *AppTcpThread(void* arg)
{
	printf("TCP Server thread start ....................");

	ServerInit();

	while (TCP_isrunning_flag)
	{
		int numClientFds = socketServerGetNumClients();

		//poll on client socket fd's for any activity
		if (numClientFds)
		{
			int pollFdIdx;
			int *client_fds = malloc(numClientFds * sizeof(int));

			//socket client FD's
			struct pollfd *pollFds = malloc((numClientFds * sizeof(struct pollfd)));

			if (client_fds && pollFds)
			{
				//Set the socket file descriptors
				socketServerGetClientFds(client_fds, numClientFds);
				for (pollFdIdx = 0; pollFdIdx < numClientFds; pollFdIdx++)
				{
					pollFds[pollFdIdx].fd = client_fds[pollFdIdx];
					pollFds[pollFdIdx].events = POLLIN | POLLRDHUP;
				}
				
				poll(pollFds, numClientFds, -1);
				for (pollFdIdx = 0; pollFdIdx < numClientFds; pollFdIdx++)
				{
					if ((pollFds[pollFdIdx].revents))
					{
						socketServerPoll(pollFds[pollFdIdx].fd,
								pollFds[pollFdIdx].revents);
					}
				}

				free(client_fds);
				free(pollFds);
			}
		}
	}
	return NULL;
}


void send_data_to_client(char *data , int len)
{
	int numClientFds = socketServerGetNumClients();					//获取客户端个数
	int *client_fds = malloc(numClientFds * sizeof(int));
	if(client_fds)
	{
		socketServerGetClientFds(client_fds, numClientFds);			//获取客户端文件描述符
		int i=0;
		for(i=1; i<numClientFds; i++)	//不发送给第一个连接
		{
			socketServerSend(data, len, client_fds[i]);	//发送数据到客户端
		}
		free(client_fds);
		client_fds = NULL;
	}
}


int app_tcp_init(void)
{
	TCP_isrunning_flag = 1;
	/*创建TCP线程*/
	if (0 != pthread_create(&ulAPPTCPThreadID, NULL, AppTcpThread, NULL))
	{
		printf("Create APPTCPThread failed!");
		return -1;
	}
	return 0;
}

void app_tcp_destroy(void)
{
	/*等待线程退出*/
	TCP_isrunning_flag = 0;
	
	if (ulAPPTCPThreadID != 0)
	{
		pthread_cancel(ulAPPTCPThreadID); //解决线程阻塞导致无法回收问题
		pthread_join(ulAPPTCPThreadID, NULL);
	}
}

如上程序,TCP接收数据包回调函数中为接收json数据示例,可自行根据具体需求进行改写。 

 app_tcp_server.h 

#ifndef __APP_TCP_SERVER__
#define __APP_TCP_SERVER__

/*TCPServer端口号*/
#define TCP_PORT 10086
#define MAX_MESSAGE_LEN 2048


/*模块初始化*/
extern int app_tcp_init(void);

/*释放资源(调用的pthread_jion)*/
extern void app_tcp_destroy(void);

#endif //__APP_TCP_SERVER__

如上所示,监听端口号设置只需修改TCP_PORT宏即可。

main.c

#include <stdio.h>
#include <unistd.h>
#include "app_tcp_server.h"
#include "socket_server.h"

int main(int argc, char *argv[])
{
	app_tcp_init();	//初始化tcp服务程序

	while(1)
	{
		sleep(1);
	}
	
	app_tcp_destroy(); //释放tcp服务程序
	
	return 0;
}

具体详细代码请参考 github地址: https://github.com/fangye945a/linux_c_example.git 中的tcp_server_example工程。

这是自己建的linux C语言示例工程,包含了许多常用的示例程序,后续将根据自身所学持续更新。如果程序有什么问题,还请大佬们多多指正~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值