在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语言示例工程,包含了许多常用的示例程序,后续将根据自身所学持续更新。如果程序有什么问题,还请大佬们多多指正~