2020/2/26 Linux Socket编程 高级篇——守护进程

守护进程

是linux后台执行的一种服务进程,特点是独立于控制终端、周期性地执行某种任务或等待处理某些发生事件,不会随终端关闭而停止,直到接受停止信息才会结束,且一般采用以d结尾的名字。

  • 守护进程(daemon)是一种生存期长的进程,常常在系统引导装入时启动,在系统关闭时终止。

  • 所有守护进程都以超级用户(用户ID为0)的优先权运行。

  • 守护进程没有控制终端

  • 守护进程的父进程都是init进程


编程步骤

  • 使用umask将文件模式创建屏蔽字设为0

  • 调用fork,然后让父进程退出(exit)

  • 调用setsid创建一个新会话

  • 将当前工作目录更改为根目录

  • 关闭不需要的文件描述符


  守护进程出错处理

  • 由于守护进程脱离了控制终端,因此,不能像其他程序一样通过输出错误信息到控制台的方式来通知程序员

  • 通常办法是使用syslog服务,将出错信息输入到“/var/log/syslog”系统日式文件中去

  • syslog是linux种的系统日志管理服务,通过守护进程syslog来维护


syslog服务声明

  • openlog函数用于打开系统日志服务的一个连接

  • syslog函数用于向日志文件中写入消息,在这里可以规定消息的优先级、消息输出格式等

  • closelog函数用于关闭系统日志服务连接

#include <syslog.h>
void openlog(char *ident, int option, inf facility);

ident:要向每个消息加入的字符串,通常为程序的名称

option:LOG_CONS       若日志消息不能发送到syslog,就写到控制台

              LOG_NDELAY   立即打开Linux域数据报套接口至syslog守护进程。通常在记录第一条消息之前,该套接口不打开

              LOG_PERROR  除将日志消息发送给syslog外,还将它写至stderr

              LOG_PID            每条消息都包含进程ID,此选项可供每个请求都fork一个子进程的守护进程使用

facility:

              LOG_AUTH         授权程序如login、su、gety等

              LOG_CRON        cron 和 at

              LOG_DAEMON   系统守护进程,如ftpd、routed等

              LOG_KERN         内核产生的消息

              LOG_LOCAL0~7 保留由本地使用

              LOG_LPR            行打系统,如lpd、lpc等

              LOG_MAIL           邮件系统

              LOG_NEWSU      senet网络新闻系统

              LOG_SYSLOG     syslog系统本身

              LOG_USER         来自其他用户的进程消息

              LOG_UUCP         UUCP系统

#include <syslog.h>
void syslog(int priority, char *format, ...);
void closelog(void);

priortity:消息优先级

LOG_EMERG       紧急(系统不可使用,最高优先级)

LOG_ALERT        必须立刻修复的条件

LOG_CRIT           临界条件(例如,硬设备出错)

LOG_ERR            出错条件

LOG_WARNING  警告条件

LOG_NOTICE      正常,但重要的条件

LOG_INFO           信息性消息

LOG_DEBUG       调试排错消息(最低优先级)

format:输出格式

...:可变参数列表:可传0个或多个参数


设置守护进程

#include <syslog.h>

umast() 需要包含头文件#include <sys/stat.h>

源代码

void do_service(int fd)
{
	char buff[512];
	memset(buff, 0, sizeof(buff));
	//因为采用非阻塞方式,若读不到数据直接返回,直接服务下一个客户端,所以不用判断size小于0的情况
	size_t size = read(fd, buff, sizeof(buff));
	if(size == 0){//客户端已经关闭连接
		syslog(LOG_DEBUG, "client closed");
		//从动态数组中删除对应的fd
		remove_fd(vfd, fd);
		//关闭对应客户端的socket
		close(fd);
	}
	else if(size > 0){	
		printf("%s\n", buff);
		if(write(fd, buff, size) < 0){//客户端关闭连接,产生EPIPE,跳出进程
			if(errno == EPIPE){
				syslog(LOG_DEBUG, "write:%s\n", strerror(errno));
				remove_fd(vfd, fd);
				close(fd);
			}
		}
	}
	
}

void out_addr(struct sockaddr_in *clientaddr)
{
	char ip[16];
	memset(ip, 0, sizeof(ip));
	int port = ntohs(clientaddr->sin_port);
	inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));
	syslog(LOG_DEBUG, "%s(%d) connected!\n", ip, port);
}

...

int main(int argc, char *argv[])
{
	if(argc < 2){
		printf("usage: %s #port\n", argv[0]);//打印程序的名字(包括路径)
		exit(1);
    }
	//守护进程的5个步骤
	//步骤1:创建屏蔽字0
	umask(0);
	//步骤2:调用fork()创建子进程,然后父进程退出
	pid_t pid = fork();
	if(pid > 0) exit(0);
	//步骤3:调用setsid()创建一个新会话
	setsid();
	//步骤4:将当前工作目录更改为根目录
	chdir("/");
	//步骤5:关闭不需要的文件描述符
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);
	
	//打开系统日志服务的一个连接
	openlog(argv[0], LOG_PID, LOG_SYSLOG);

	/*step 1 creat socket
	 *socket is a struct in kernel
	 *AF_INET: IPV4
	 *SOCK_STREAM: tcp protocol(udp: SOCK_DGRAM)
	 **/
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
	//将日志信息写入到系统日志文件中(/var/log/syslog)
		syslog(LOG_DEBUG, "socket:%s\n", strerror(errno));
		exit(1);
	}

	/*step 2 bind()
	 *bound with socket and address(ip\port\intnet type)
     *sockaddr_in is special net_struct for internet */
	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(serveraddr));  //clear
	serveraddr.sin_family = AF_INET;	 	     //IPV4(Host byte order is ok)
	serveraddr.sin_port   = htons(atoi(argv[1]));//port from terminal(atoi:string to int)(htons:Host to Network byte order 16bit)
	serveraddr.sin_addr.s_addr = INADDR_ANY;     //htons("192.168.0.10")
	if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){//cast as common address type : sockaddr
		syslog(LOG_DEBUG, "bind:%s\n", strerror(errno));
		exit(1);
 	}

	/*step 3 listen()
	 *tell system to accept connecting request (in server port)
	 *put connecting request to queue*  (10 is the length of queue)
	 */
	if(listen(sockfd, 10) < 0){
		syslog(LOG_DEBUG, "listen:%s\n", strerror(errno));
		exit(1);	
	}
	
	/*step 4 accept()
	 *get a connection and return the new socket file descriptor(sockfd)
	 *This sockfd(client's fd) is different from the sockfd in step 1(server's fd)
	 */
	

	//创建放置套接字描述符fd的动态数组
	vfd = create_vector_fd();
	//设置线程的分离属性
	pthread_t th;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);


...

}

运行结果

 

查看当前进程&杀掉某一进程

查看系统日志(不分屏):cat /var/log/syslog 

查看系统日志(从尾部开始查看):tail /var/log/syslog 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴能够回答你的问题。以下是一个简单的即时通信系统的C++网络编程示例,基于Linux平台使用socket库进行开发。 客户端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接服务器 int connect_result = connect(client_socket, (sockaddr *) &server_address, sizeof(server_address)); if (connect_result == -1) { cout << "连接服务器失败!" << endl; return -1; } // 发送消息 char message[1024] = {0}; while (true) { cin.getline(message, sizeof(message)); if (strlen(message) == 0) { continue; } send(client_socket, message, strlen(message), 0); memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); return 0; } ``` 服务器端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 绑定socket和地址 bind(server_socket, (sockaddr *) &server_address, sizeof(server_address)); // 监听客户端连接 listen(server_socket, 10); // 接受客户端连接 sockaddr_in client_address{}; socklen_t client_address_len = sizeof(client_address); int client_socket = accept(server_socket, (sockaddr *) &client_address, &client_address_len); // 接收消息 char message[1024] = {0}; while (true) { int recv_result = recv(client_socket, message, sizeof(message), 0); if (recv_result <= 0) { break; } cout << "收到消息:" << message << endl; memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); close(server_socket); return 0; } ``` 在上面的代码中,客户端和服务器端都使用了socket库中的函数来完成网络通信。客户端使用`socket`函数创建socket,使用`connect`函数连接服务器,并使用`send`函数发送消息。服务器端使用`socket`函数创建socket,使用`bind`函数将socket与服务器地址绑定,使用`listen`函数监听客户端连接,并使用`accept`函数接受客户端连接,最后使用`recv`函数接收客户端发送的消息。 当然,这只是一个简单的示例。在实际开发中,你需要考虑更多的细节,例如如何处理多个客户端连接、如何处理异常情况等等。 希望这个示例能够帮助你了解Linux下C++ socket网络编程的基本流程和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值