📄 server.c
字号:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<netinet/in.h> /*sockaddr_in 结构体*/
#include<signal.h>
#include<arpa/inet.h>
#include<sys/ioctl.h>
#include<net/ethernet.h>
#include<net/if.h>
#include<net/if_arp.h>
/*下面的这个include可以使程序查主机名列表,找出所有与其IP绑定的hostname*/
#include<netdb.h>
#include<sys/stat.h>
#include<unistd.h>
#include <time.h>
#include <pthread.h>
#include <fcntl.h>
#include "common.h"
#include "avl.h"#include "fee.h"
#define SERVER_TIMER_INTER 1
static struct flow_info *flow_list = NULL;
static struct avl_instance avltree;
static struct avl_node *free_node = NULL;
static struct avl_node *used_node = NULL;
pthread_t timerid = 0;
pthread_mutex_t lockavl;
void do_exit();
int init_config();
void free_flowlist();
void dump_flowlist();
int check_flowlist(unsigned int, int);
struct flow_info *find_flowlist(unsigned int);
void dump_usedlist();
void daemon_init();
void *server_timer(void *);
void usage(char *prog)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\t%s [-v | -h] [port]\n", prog);
exit(1);
}
int main(int argc, char*argv[])
{
int rc, /*系统调用return code*/
new_sd,sock, /*server/listen套接字标识符*/
adrlen, /*sockaddr长度*/
cnt; /*number of bytes I/O*/
struct sockaddr_in myname;
struct sockaddr_in *nptr; /*ptr获取端口号*/
struct sockaddr addr;/*通用套接字名称*/
char file[256];
char buf[65535]; /*I/O缓冲区定义*/
DEBUG("Server Starting......\n");
FILE *fp = NULL;
struct stat file_stat;
int node_num = 0;
int ret = -1;
int verbose = 0;
int server_port = SERVER_PORT;
/* 在/etc/hosts文件中查找 */
struct hostent *hp;
struct hostent *gethostbyaddr();
/* 标识服务器进程,打印其 PID */
signal(SIGTERM, (void *)do_exit);
signal(SIGINT, (void *)do_exit);
switch (argc) {
case 1:
verbose = 0;
break;
case 2:
if (!strcmp(argv[1], "-v"))
verbose = 1;
else
server_port = atoi(argv[1]);
break;
case 3:
if (!strcmp(argv[1], "-v")) {
verbose = 1;
server_port = atoi(argv[2]);
break;
}
default:
usage(argv[0]);
}
if (!verbose)
daemon_init();
if (init_config() < 0) {
DEBUG("init config error");
exit(1);
}
if (init_avl_tree(&avltree, &node_num, &free_node,
AVL_NODE_SIZE, MAX_LINK_NUM, NODE_KEY_LEN) < 0) {
DEBUG("init avl tree error, arg : %d %d %d", AVL_NODE_SIZE, MAX_LINK_NUM, NODE_KEY_LEN);
exit(1);
}
DEBUG("init avl tree ok, get avl node num : %d", node_num);
pthread_create(&timerid, NULL, server_timer, NULL);
printf("\nThis is the network server with pid %d\n",getpid());
/*建立一个倾听套接字*/
if ((sock=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("network server socket failure %d\n",errno);
perror("network server");
exit(1);
}
/*初始化套接字地址结构中的各个参数*/
myname.sin_family = AF_INET; /*Internet地址*/
myname.sin_port = htons(server_port); /*系统会绑定端口号*/
myname.sin_addr.s_addr = INADDR_ANY;
/* 将IP地址结构与套接字绑定,调用bind()函数*/
if (bind(sock, (struct sockaddr *)&myname,sizeof(myname))<0) {
close(sock); /*defensive programming */
printf("network server bind failure %d\n",errno);
perror("network server");
exit(2);
}
/* 获取套接字端口号.调用getsockname()可以得到与套接字相关联的端口号,
并作为套接字地址结构中的一部分返回.记录这个端口号以供客户端程序使用.
*/
adrlen=sizeof(addr);/*地址长度为整形值*/
if ((rc=getsockname(sock,&addr,&adrlen))<0)
{
printf("setwork server getsockname failure %d\n",errno);
perror("network server");
close(sock);
exit(3);
}
/*调试说明:
通用套接字地址addr被用来存放getsockname()返回的套接字的值.
打印这个信息.在一般的地址声明中,除了地址族其余的都被定义为字符串类型.
Getsockname()调用之后,这个通用套接字地址结构addr就会被用来存放关于客户端进程
的信息
*/
printf("\nAfter getsockname():");
printf("server listen socket data\n");
printf("\taddr.sa_family field value is:%d\n",addr.sa_family);
printf("\taddr.sa_data string is %d bytes long;\n",sizeof(addr.sa_data));
printf("\taddr.sa_data string is");
for (cnt=0;cnt<sizeof(addr.sa_data);cnt++)
printf("%x",addr.sa_data[cnt]);
printf("\n");
/* 现在需要记录下来套接字的端口号.在这里端口号将作为启动客户端程序的一个
命令行参数使用.
注意指针nptr,它是指向通用套接字地址结构的一个指针
*/
nptr=(struct sockaddr_in *)&addr; /*port*/
printf("\n\tnetwork server:server has port number:%d\n",
ntohs(nptr->sin_port));
/*调用listen()函数*/
if(listen(sock,5)<0)
{
printf("network server bind failure %d\n",errno);
perror("network server");
close(sock);
exit(4);
}
/*调试输出:
下面输出结构体myname中所包含的套接字信息 */
printf("Server has set up client socket with values:\n");
printf("\tInternet address is %s\n",inet_ntoa(myname.sin_addr));
printf("\tPort number used is %d\n",myname.sin_port);
printf("\tInternet family ID is %d\n",myname.sin_family);
printf("\tValues are filled in after connection request is accepted.");
/*设置初始死循环以等待客户端进程的连接亲请求.由于结构体myname已经和倾听套接字绑定,通过accept()调用可以获得套接字结构名称和套接字长度参数.
*/
while(1) {
if ((new_sd=accept(sock,0,0))<0){
printf("network server accept failure %d\n",errno);
perror("network server");
close(sock);
exit(5);
}
/*通过fork()函数建立子进程来处理客户端服务请求.*/
// if ((fork())==0) { /*子进程*/
{ /*子进程*/
int pid;
pid=getpid(); /*获取子进程的PID 0*/
// close(sock); /*子进程中不再需要父进程的参与 */
/*查找客户端所在.注意通用套接字地址结构addr的用法,获取客户端信息 */
if((rc=getpeername(new_sd,&addr,&adrlen))<0) {
printf("network server %d getpeername failure %d\n",
pid,errno);
perror("network server");
close(new_sd);
exit(6);
}
/*答应客户端信息.由于指针nptr是一个sockaddr_in结构体指针,
在这个结构体中的地址名称就可以传递给通用地址结构体addr.
*/
printf("\n\tnetwork server %d:",pid);
printf("client socket from host %s\n",inet_ntoa(nptr->sin_addr));/*?*/
printf("\t has port number %d\n",nptr->sin_port);
/*现在找到所有与客户端有关的信息,并且可以通过在/etc/hosts中
查找来确定客户端的域名 */
if((hp=gethostbyaddr(&nptr->sin_addr,4,AF_INET))!=NULL) {
printf("\tfrom hostname:%s\n\twith aliases:",hp->h_name);
while(*hp->h_aliases)
printf("\n\t\t\t%s",*hp->h_aliases++);
printf("\n\n");
} else {
printf("network server%d",pid);
printf("gethostbyaddr failure %d\n",h_errno);
perror("network server");
}
/*与客户端交换数据.首先清除缓冲区*/
do {
memset(file,0,sizeof(file)); /*清空缓冲区*/
/*用read()函数从套接字缓冲区中读取远程客户端发送的数据,如果数据长度为0*/
if((cnt=read(new_sd, file,sizeof(file)))<0){
printf("network server %d",pid);
printf("socket read failure %d\n",errno);
perror("network server");
close(new_sd);
exit(7);
} else if (cnt==0) {
printf("network server received message ");
printf("of length %d\n",cnt);
printf("network server closing client connection...\n");
close(new_sd);
break; /*循环终止点*/
} else {
/*打印出接收到的信息,并发回执 */
printf("network server %d received message ",pid);
printf("of length %d \n",cnt);
printf("network server %d received ",pid);
printf("the message %s\n", file); /*清空缓冲区*/
memset(buf, 0, sizeof(buf));
if (access(file, F_OK) != 0) {
DEBUG("file %s not exist", file);
strcpy(buf, "File not found.\n");
write(new_sd,buf,sizeof(buf));
close(new_sd);
break;
}
if (stat(file, &file_stat) < 0) {
DEBUG("stat file %s error", file);
strcpy(buf, "Get file statistic info error.\n");
write(new_sd,buf,sizeof(buf));
close(new_sd);
break;
}
fp = fopen(file, "r");
if (fp == NULL) {
DEBUG("open file %s error", file);
strcpy(buf, "Open file error.\n");
write(new_sd,buf,sizeof(buf));
close(new_sd);
break;
}
fread(buf, file_stat.st_size, 1, fp);
fclose(fp);
ret = check_flowlist(ntohl(nptr->sin_addr.s_addr), file_stat.st_size);
switch (ret) {
case 0:
DEBUG("check flow list ok");
break;
case 1:
strcpy(buf, "Flow limit, request forbidden.\n");
DEBUG("%s", buf);
break;
case 2:
strcpy(buf, "No rule, default forbidden.\n");
DEBUG("%s", buf);
break;
case 3:
strcpy(buf, "Reach max link num, forbidden.\n");
DEBUG("%s", buf);
break;
default:
strcpy(buf, "Unknown error, forbidden.\n");
DEBUG("%s", buf);
break;
}
if (ret != 0) {
write(new_sd,buf,strlen(buf));
close(new_sd);
break;
}
dump_usedlist();
write(new_sd,buf,file_stat.st_size);
}
} while (cnt != 0); /* end of do loop */
// exit(0);
}
// } else /*End of if-child-process */ /*非子进程,父进程条件判断*/
// close(new_sd); /*父进程不需要此套接字*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -