Clamav杀毒软件源码分析笔记[五]
刺猬@http://blog.csdn.net/littlehedgehog
[Socket通信]
套间字socket常用于服务/客户模型的应用程序之间的通信和数据连接,需要重点关注的是这个所谓的服务端/客户端完全可以是一台计算机的两个应用程序。前面我们已经提到了,Clamd就是这个服务端,也就是说它是整个程序的顶梁柱,所有的关键逻辑处理都是它一人为之,不可不谓之强悍。所有的这些功劳都应该归功于Clamd中的线程处理,不过我们先还不是来关注线程的,先看看socket的建立。
- int localserver(const struct optstruct *opt, const struct cfgstruct *copt, struct cl_node *root)
- {
- struct sockaddr_un server;
- int sockfd, backlog;
- struct cfgstruct *cpt;
- struct stat foo;
- char *estr;
- /* 下面是初始化套间字 顺便贴上了配置文件中有关localsocket的信息
- # Path to a local socket file the daemon will listen on.
- # Default: disabled
- LocalSocket /tmp/clamd
- */
- memset((char *) &server, 0, sizeof(server));
- server.sun_family = AF_UNIX;
- strncpy(server.sun_path, cfgopt(copt, "LocalSocket")->strarg, sizeof(server.sun_path));
- if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- {
- estr = strerror(errno);
- /*
- fprintf(stderr, "ERROR: socket() error: %s/n", estr);
- */
- logg("!Socket allocation error: %s/n", estr);
- exit(1);
- }
- if (bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1)
- {
- if (errno == EADDRINUSE) //端口号或者socket被其他进程使用
- {
- if (connect(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) >= 0) //这里连接成功表明socket确实是被别的进程使用了
- {
- close(sockfd);
- logg("!Socket file %s is in use by another process./n", server.sun_path);
- exit(1);
- }
- if (cfgopt(copt, "FixStaleSocket")) //该socket不是被别的进程占据了,下面我们要做修复工作,配置文件中有FixStaleSocket 意思是要修理失效的socket
- {
- logg("^Socket file %s exists. Unclean shutdown? Removing.../n", server.sun_path);
- if (unlink(server.sun_path) == -1) //从文件系统中删除一个名称。如果名称是文件的最后一个连接,并且没有其它进程将文件打开,名称对应的文件会实际被删除。
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be removed: %s/n", server.sun_path, estr);
- exit(1);
- }
- if (bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1) //再次bind如果失败
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be bound: %s (unlink tried)/n", server.sun_path, estr);
- exit(1);
- }
- }
- else if (stat(server.sun_path, &foo) != -1)
- {
- logg("!Socket file %s exists. Either remove it, or configure a different one./n", server.sun_path);
- exit(1);
- }
- }
- else //这里出现了其他错误
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be bound: %s/n", server.sun_path, estr);
- exit(1);
- }
- }
- logg("Unix socket file %s/n", server.sun_path);
- if ((cpt = cfgopt(copt, "MaxConnectionQueueLength"))) //Maximum length the queue of pending connections may grow to. backlog参数为提出连接请求后,在服务器接收该连接请求时的等待队列中的连接数
- backlog = cpt->numarg;
- else
- backlog = CL_DEFAULT_BACKLOG;
- logg("Setting connection queue length to %d/n", backlog);
- if (listen(sockfd, backlog) == -1)
- {
- estr = strerror(errno);
- /*
- fprintf(stderr, "ERROR: listen() error: %s/n", estr);
- */
- logg("!listen() error: %s/n", estr);
- exit(1);
- }
- acceptloop_th(sockfd, root, copt);
- return 0;
- }
这里我们主要就是建立个socket的,其实也没啥好说的,主要是整个函数的调用,将改变整个历史进程方向──acceptloop_th(sockfd, root, copt); 因为我们要被一直束缚在里面,貌似永世不得超生了。
acceptloop_th 很长很强大,所以我也就不把源码贴出来了,否则有凑字数的嫌疑,重点关注下下面一块:
- /* set up signal handling */
- sigfillset(&sigset);
- sigdelset(&sigset, SIGINT);
- sigdelset(&sigset, SIGTERM);
- sigdelset(&sigset, SIGSEGV);
- sigdelset(&sigset, SIGHUP);
- sigdelset(&sigset, SIGPIPE);
- sigdelset(&sigset, SIGUSR2);
- sigprocmask(SIG_SETMASK, &sigset, NULL); //通过这条语句 进程有了新的信号屏蔽集 这里是屏蔽一些我们不关心的信号
- /* SIGINT, SIGTERM, SIGSEGV */
- sigact.sa_handler = sighandler_th;
- sigemptyset(&sigact.sa_mask);
- sigaddset(&sigact.sa_mask, SIGINT);
- sigaddset(&sigact.sa_mask, SIGTERM);
- sigaddset(&sigact.sa_mask, SIGHUP);
- sigaddset(&sigact.sa_mask, SIGPIPE);
- sigaddset(&sigact.sa_mask, SIGUSR2);
- sigaction(SIGINT, &sigact, NULL); //下面都是安装新的信号处理程序
- sigaction(SIGTERM, &sigact, NULL);
- sigaction(SIGHUP, &sigact, NULL);
- sigaction(SIGPIPE, &sigact, NULL);
- sigaction(SIGUSR2, &sigact, NULL);