最大连接数的限制
在session结构体重增加一个num_clients成员,在主线程序中,每当来一个客户端就使num_clients++。子进程在开启会话前要进行最大连接数限制的检查,只有小于最大连接数的限制才开启会话。那么子进程退出的时候如何对num_clients进行维护呢?
void check_limits(session_t *sess)
{
if(sess->num_clients > tunable_max_clients)
{
//421 There are too many connected users, please try later.
ftp_reply(sess, FTP_TOO_MANY_USERS, "There are too many connected users, please try later.");
exit(EXIT_FAILURE);
}
}
每IP数登录的限制
要实现每IP数登录的限制,需要实现两个hash表:ip–count表和pid–ip表
实现哈希表:
hash.h
#ifndef _HASH_H_
#define _HASH_H_
typedef struct hash hash_t;
typedef unsigned int (*hashfunc_t)(unsigned int, void*);
hash_t* hash_alloc(unsigned int buckets, hashfunc_t hash_func);
void* hash_lookup_entry(hash_t *hash, void* key, unsigned int key_size);
void hash_add_entry(hash_t *hash, void *key, unsigned int key_size,
void *value, unsigned int value_size);
void hash_free_entry(hash_t *hash, void *key, unsigned int key_size);
#endif
hash.c
#include"hash.h"
#include "hash.h"
#include "common.h"
#include <assert.h>
typedef struct hash_node
{
void *key;
void *value;
struct hash_node *prev;
struct hash_node *next;
} hash_node_t;
struct hash
{
unsigned int buckets;
hashfunc_t hash_func;
hash_node_t **nodes;
};
hash_node_t** hash_get_bucket(hash_t *hash, void *key);
hash_node_t* hash_get_node_by_key(hash_t *hash, void *key, unsigned int key_size);
hash_t *hash_alloc(unsigned int buckets, hashfunc_t hash_func)
{
hash_t *hash = (hash_t *)malloc(sizeof(hash_t));
//assert(hash != NULL);
hash->buckets = buckets;
hash->hash_func = hash_func;
int size = buckets * sizeof(hash_node_t *);
hash->nodes = (hash_node_t **)malloc(size);
memset(hash->nodes, 0, size);
return hash;
}
void* hash_lookup_entry(hash_t *hash, void* key, unsigned int key_size)
{
hash_node_t *node = hash_get_node_by_key(hash, key, key_size);
if (node == NULL)
{
return NULL;
}
return node->value;
}
void hash_add_entry(hash_t *hash, void *key, unsigned int key_size,
void *value, unsigned int value_size)
{
if (hash_lookup_entry(hash, key, key_size))
{
fprintf(stderr, "duplicate hash key\n");
return;
}
hash_node_t *node = malloc(sizeof(hash_node_t));
node->prev = NULL;
node->next = NULL;
node->key = malloc(key_size);
memcpy(node->key, key, key_size);
node->value = malloc(value_size);
memcpy(node->value, value, value_size);
hash_node_t **bucket = hash_get_bucket(hash, key);
if (*bucket == NULL)
{
*bucket = node;
}
else
{
// 将新结点插入到链表头部
node->next = *bucket;
(*bucket)->prev = node;
*bucket = node;
}
}
void hash_free_entry(hash_t *hash, void *key, unsigned int key_size)
{
hash_node_t *node = hash_get_node_by_key(hash, key, key_size);
if (node == NULL)
{
return;
}
free(node->key);
free(node->value);
if (node->prev)
{
node->prev->next = node->next;
}
else
{
hash_node_t **bucket = hash_get_bucket(hash, key);
*bucket = node->next;
}
if (node->next)
node->next->prev = node->prev;
free(node);
}
hash_node_t** hash_get_bucket(hash_t *hash, void *key)
{
unsigned int bucket = hash->hash_func(hash->buckets, key);
if (bucket >= hash->buckets)
{
fprintf(stderr, "bad bucket lookup\n");
exit(EXIT_FAILURE);
}
return &(hash->nodes[bucket]);
}
hash_node_t* hash_get_node_by_key(hash_t *hash, void *key, unsigned int key_size)
{
hash_node_t **bucket = hash_get_bucket(hash, key);
hash_node_t *node = *bucket;
if (node == NULL)
{
return NULL;
}
while (node != NULL && memcmp(node->key, key, key_size) != 0)
{
node = node->next;
}
return node;
}
定义两个hash表:
s_ip_count_hash = hash_alloc(193, hash_func);
s_pid_ip_hash = hash_alloc(193, hash_func);
在session会话结构体中添加num_this_ip成员。在主线程序中,父进程登记pid–IP表。在主线程序中,来一个客户端:
unsigned int handle_ip_count(void *ip)
{
unsigned int count;
unsigned int *p_count = (unsigned int *)hash_lookup_entry(s_ip_count_hash, ip, sizeof(unsigned int));
if (p_count == NULL)//查找出来的p_count为空说明是新的IP,将其count置为1,插入hash表
{
count = 1;
hash_add_entry(s_ip_count_hash, ip, sizeof(unsigned int),&count, sizeof(unsigned int));
}
else//否则给对应的count++
{
count = *p_count;
++count;
*p_count = count;
}
return count;
}
那么在ftp进程退出的时候,如何维护呢?之前的进程模型,对子进程结束之后发出的信号设置忽略,防止产生僵尸进程。现在对该进程模型进行一定的修改:
signal(SIGCHLD, handle_sigchld);
主线进程收到子进程结束发来的SIGCHLD信号会执行handle_sigchld函数。
void handle_sigchld(int sig)
{
pid_t pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0)//回收子进程
{
client_counts--;//总的最大连接数减一
//通过pid查找IP
unsigned int *ip = hash_lookup_entry(s_pid_ip_hash, &pid, sizeof(pid));
if (ip == NULL)//???
{
continue;
}
//减少IP对应的count
drop_ip_count(ip);
//然后释放该pid--IP节点
hash_free_entry(s_pid_ip_hash, &pid, sizeof(pid));
}
}
void drop_ip_count(void *ip)
{
unsigned int count;
//通过IP查表得到对应count
unsigned int *p_count = (unsigned int *)hash_lookup_entry(s_ip_count_hash, ip, sizeof(unsigned int));
if (p_count == NULL)
{
return;
}
//count--
count = *p_count;
--count;
*p_count = count;
//如果count减少到0,则释放IP--count节点
if (count == 0)
{
hash_free_entry(s_ip_count_hash, ip, sizeof(unsigned int));
}
}