⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 server.c

📁 本Linux网络应用程序采用客户-服务器模型,并发型交互。在OSI参考模型的传输层,通过调用TCP套接字(Socket)的各种函数,使服务器和各个客户端之间建立快速可靠的连接
💻 C
📖 第 1 页 / 共 2 页
字号:
#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 + -