大型系统中socket集中管理及select非阻塞读写

在小型系统中有可能只需要一个或者两个为数不多的socket即可满足需求,但是一个很大的系统,比方说需要20个人连续开发两三年的一个网络通信系统,这样的大系统不管是系统实际运用还是trace message的使用都必不可少的应用到socket,这样一个系统中就存在较多的socket,如果没有一个集中式的模块进行管理的话,这样开发系统的每个需要用到socket的人员都需要进行socket的集中学习并且各自不同的socket放到一个系统中严重影响可读性、降低系统性能和提高解决bug的工作量;因此,大型系统中对于socket集中管理并实现非阻塞操作至关重要。

针对这种情况,我们将所有的socket(包括tcp和udp)进行人性化的select无阻塞管理,在创建socket的时候根据用户不同的需求进行管理。可以设置一个标志位flag用来表示用户的需求,如果flag=TOGETHER,将此socket同一进行管理,监听read和write文件描述符,如果read描述符有数据则调用结构体中的接收函数指针进行消息的解析及处理,如果socket可写,就从socket对应的缓冲队列中取出数据进行发送;否则flag=SINGLE,此socket不纳入统一管理;

首先定义相关的的socket模块的结构体,对应所有的socket可以分为UDP, TCP SERVER 和TCP CLIENT
/*为了对用户屏蔽具体socket实现,定义此接收函数指针*/
typedef int (*SOCK_FUNC)(int index, int *msg_p, int msg_len);//the pointer of function

typedef struct MsgNode {
int msg_len;
char *msg_p;
struct MsgNode *next;
}MsgNode_t;

typedef struct{
int client_conn;
MsgNode_t *msg_queue;
}ClientInfo;

typedef struct {
int udp_index; // form 0 to MAX_UDP SOCKET-1
int socket_conn; // the socket indication of system.
struct sockaddr_in send_addr; //the address that the message will be sended to.
SOCK_FUNC rx_deal_func; //the receive function
SocketRxType rx_type; //the type,TOGETHER or SINGLE
MsgNode_t *msg_queue; //store the messages which will be sended.
} UdpSocket;

typedef struct {
int server_index; // form 0 to MAX_TCP_SERVER-1
ClientInfo client_info[MAX_TCP_USER]; // store the server's clients.
int server_socket;
int current_num; // the server has current_num+1 clients now.
SOCK_FUNC rx_deal_func;
MsgNode_t *msg_queue;
} TcpServerSocket;

typedef struct {
int client_index; // form 0 to MAX_TCP_CLIENT-1
int server_socket; //store the socket of tcp server
SOCK_FUNC rx_deal_func;
SocketRxType rx_type;
MsgNode_t *msg_queue;
} TcpClientSocket;


创建socket时需要提供几个必须的参数参数,
例如创建UDP时,为了简便,本设计中socket与发送地址进行绑定,当然你可以通过自己编写函数实现socket与发送地址的解耦,动态改变发送地址
NT32 create_udp_socket(UINT8 *send_addr, INT32 send_port, INT32 recv_port, SOCK_FUNC rx_deal_func, SocketRxType rx_type)
send_addr : 本socket数据的发送目的地址ip
send_port : 本socket数据的发送目的地址port
recv_port : 本socket监听的本地的port号
rx_deal_func : 收到消息后,调用的函数指针
rx_type : 期望对socket处理,取值为TOGETHER(集中管理),SINGLE(独自管理)


创建完socket时候需要将一些socket进行监听,例如
if (SOCKET_TOGETHER == rx_type) {
if (g_udp_socket[current_udp_num].socket_conn > max_socket) {
max_socket = g_udp_socket[current_udp_num].socket_conn; //store the max_sock which is needed in the select function
}
FD_SET(g_udp_socket[current_udp_num].socket_conn, &read_set);
FD_SET(g_udp_socket[current_udp_num].socket_conn, &write_set);
}


下面就是关键的select处理了
fd_set rd_set,wt_set;
struct timeval timeout = {0, 10}; //第一个单位为秒,第二个单位为毫秒

while (1) {
rdy_set = read_set;
wt_set = write_set;


if (select(max_socket+1, &rd_set, &wt_set, NULL, &timeout) > 0) {


/*receive tcp server socket.*/
for (i = 0; i < MAX_TCP_SERVER; i++) {
if (g_tcp_server_socket[i].server_socket == 0) //the server socket is not exit
continue;
if (FD_ISSET(g_tcp_server_socket[i].server_socket, &rd_set)) { //the server socket is exit,need to accept
/*find a free loaction of the client socket*/

if (user_flag == 1) {
client_conn = accept(g_tcp_server_socket[i].server_socket,
(struct sockaddr_in *)&ue_conn_addr, &addr_len);
if (-1 == g_tcp_server_socket[i].client_socket[user_index]) {
log_msg(LOG_ERR, HENB_TNL, "Error accetp UE connect:%s", strerror(errno));
exit(1);
}else{
log_msg(LOG_SUMMARY, HENB_TNL,"Welcome ue[%s] idx:%d connecting to HeNB\n",
inet_ntoa(ue_conn_addr.sin_addr), user_index);
}
//add to management
FD_SET(client_conn, &read_set);
FD_SET(client_conn, &write_set);
if (client_conn > max_socket)
max_socket = client_conn;


/*need to store client_conn to the associated struct*/
g_tcp_server_socket[i].current_num = (g_tcp_server_socket[i].current_num + 1) % MAX_TCP_USER;
}
}

for (j = 0; j < MAX_TCP_USER; j++) {
/*read message*/
if (g_tcp_server_socket[i].client_socket[j]!= 0 && FD_ISSET(g_tcp_server_socket[i].client_info[j].client_conn, &rd_set)) {
ret = rx_tcp_server_msg(i, j, &msg_p, &msg_len); 
/*i is the index of tcp server and j is the client in the server*/

g_tcp_server_socket[i].rx_deal_func(j, msg_p, msg_len); //use the pointer of the receive function
} //if FD_ISSET

/*send message*/
if (g_tcp_server_socket[i].client_socket[j]!= 0 && FD_ISSET(g_tcp_server_socket[i].client_info[j].client_conn, &wt_set)) {
if(NULL != g_tcp_server_socket[i].client_info[j].msg_queue) {
ret = send_tcp_server_msg(i, j, g_tcp_server_socket[i].client_info[j].msg_queue.msg_p, g_tcp_server_socket[i].client_info[j].msg_queue.msg_len);

/*need add the management of the msg_queue*/
}
} //if FD_ISSET
} //for the tcp server's client
} // for the tcp server


/*receive tcp client*/
for (i = 0; i < MAX_TCP_CLIENT; i++) {

/*recevie message*/
if (g_tcp_client_socket[i].server_socket !=0 && g_tcp_client_socket[i].rx_type == SOCKET_TOGETHER &&
FD_ISSET(g_tcp_client_socket[i].server_socket, &rd_set)) {
ret = rx_tcp_client_msg(i,&msg_p, &msg_len);

g_tcp_client_socket[i].rx_deal_func(i, msg_p, msg_len);
} //if FD_ISSET

/*send message*/
if (g_tcp_client_socket[i].server_socket !=0 && g_tcp_client_socket[i].rx_type == SOCKET_TOGETHER &&
FD_ISSET(g_tcp_client_socket[i].server_socket, &wt_set)) {
if(NULL != g_tcp_client_socket[i].msg_queue) {
send_tcp_client_msg(i, g_tcp_client_socket[i].msg_queue.msg_p, g_tcp_client_socket[i].msg_queue.msg_len);

/*need add the management of the msg_queue*/
}
} //if FD_ISSET
} //for the tcp client


/*receive udp client*/
for (i = 0; i < MAX_UDP_NUM; i++) {
if (g_udp_socket[i].socket_conn !=0 && g_udp_socket[i].rx_type == SOCKET_TOGETHER &&
FD_ISSET(g_udp_socket[i].socket_conn, &rd_set)) {
ret = rx_udp_msg(i, &msg_p, &msg_len);
g_udp_socket[i].rx_deal_func(i, msg_p, msg_len);
} //if FD_ISSET

/*send message*/
if (g_udp_socket[i].socket_conn !=0 && g_udp_socket[i].rx_type == SOCKET_TOGETHER &&
FD_ISSET(g_udp_socket[i].socket_conn, &wt_set)) {
if(NULL != g_udp_socket[i].msg_queue) {
send_udp_msg(i, g_udp_socket[i].msg_queue.msg_p, g_udp_socket[i].msg_queue.msg_len);
/*need add the management of the msg_queue*/
}
} //if FD_ISSET
} //for the tcp client


} //if select
}


在上面的代码中只是对于过程进行了大致的描述,只是对于流程的一个梳理过程,并没有进行完备性的验证;


这样设计完成后socket管理模块管理整个系统中的所有的socket,用户如果想使用socket,只需要调用模块提供的接口,如果发送数据也只需要调用发送接口,使得socket的实现对于用户完全屏蔽,这样可以减少由于整个系统的开发时间,并且通过集中管理,可以在出现错误时集中进行修复,节省了开发时间并提高了开发效率
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值