Linux网络编程之客户端由Socket执行服务端程序

最近由于工作原因需要温习一下Linux网络编程的部分基础知识,因此对之前写的Socket网络通信的代码进行了进一步优化和拓展,在不关闭一次Socket连接的基础上,对服务端加入循环读写的功能,同时加入连接检测函数,防止客户端意外断开(例如Ctrl + C杀掉客户端程序),服务端程序陷入死循环。增加客户端退出命令(服务端亦接收此命令),增加客户端执行服务端程序的命令,这里主要用到了strncasecmp和system函数。

最近看同事代码时看到了C程序中使用回调函数,为了加强对回调函数的理解,于是简单尝试想用回调函数调用系统read和write函数,但后来发现第三个参数不兼容,编译时告警(虽然可以通便编译,正常运行),但最后只能作罢,将read函数和write的回调函数分别进行了编辑。关于回调函数,这篇博文写的比较浅显易懂,可以作为入门讲解:

https://www.cnblogs.com/jiangzhaowei/p/9129105.html

服务端程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include<netinet/tcp.h>

#define MAX_LINE 100
/* 处理函数,用于将大写字符转换为小写字符。参数为需要转换的字符串 */
void upper_to_lower(char *str)
{
	if (str == NULL)				 /* 空串 */
		return;
	for (; *str != '\0'; str++)
	{
		if(*str >= 'A' && *str <= 'Z') /* 判断字符并进行转换,也可以使用库函数 */
			*str = *str -'A' + 'a'; 
	}
}

/* 判断接收的字符串是否为定义的命令,若是则执行相应的操作 */
int msg_to_cmd(char *str)
{
        int len = strlen(str);
        if (strncasecmp(str, "excute", len) == 0)
        {
                cmd_excute("~/linux/helloWorld"); //执行指定目录下的helloWorld应用程序
		        return -1;
        }
        else if (strncasecmp(str ,"quit", len) == 0)
        {
                return 1;
        }
        else
        {
        }
        return 0;
}

/* 封装Linux系统命令调用函数system */
int cmd_excute(char *cmd)
{
	printf("Excute command: %s\n",cmd);
	system(cmd);
	return 0;
}

/*TCP连接检测函数,用于检测客户端是否异常断开,防止服务端socket程序崩溃*/
int connect_detect(int fd)
{
    struct tcp_info info;
    int len = sizeof(info);
 
    if (fd <= 0) return 0;
 
    getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
    /*if (info.tcpi_state == TCP_ESTABLISHED) */
    if (info.tcpi_state == 1)  // Is connected
        return 0;
    else  // Losed
        return 1;
}

/*封装write函数的回调函数,增加打印功能 */
int write_callback(int *fd, const void *buf_ptr, size_t len, ssize_t (*wr)(int, const void*, size_t))
{
	int res = 0;
	res = wr(*fd, buf_ptr, len);
	if (res != -1)
        {
        	printf("Write message succeed !\n");
        }
        else
        {
        	printf("Write message failed !\n");
       	}
		//res = wr(*fd, buf_ptr, len);
		//printf("Receive Message: %s\n", buf_ptr);
	return res;
}

/* 由于read函数的第二个参数非const,因此无法与write函数使用同一回调函数,否则编译器会报警 */
int read_callback(int *fd, void *buf_ptr, size_t len, ssize_t (*rd)(int, void*, size_t))
{
	int count = 0;
	count = rd(*fd, buf_ptr, len);
	printf("Receive message: %s\n", buf_ptr);
	return count;
}

int main(void)
{
	struct sockaddr_in sin;
	struct sockaddr_in cin;
	int l_fd;
	int c_fd;
	socklen_t len;
	char buf[MAX_LINE];					/* 存储传送内容的缓冲区 */
	char addr_p[INET_ADDRSTRLEN];		/* 存储客户端地址的缓冲区 */
	int port = 80000;					/* 端口号,使用8000 */

	bzero(&sin, sizeof(sin));			/* 清空地址结构 */
	sin.sin_family      = AF_INET;			/* 使用IPv4通信域 */
	sin.sin_addr.s_addr = INADDR_ANY;	/* 服务器可以接受任意地址 */
	sin.sin_port        = htons(port);		/* 端口号转换为网络字节序 */
	l_fd                = socket(AF_INET, SOCK_STREAM, 0); /* 创立套接字,使用TCP协议 */
	bind(l_fd, (struct sockaddr*) &sin, sizeof(sin)); /* 将地址和套接字绑定 */
	listen(l_fd, 10);						/* 开始监听连接请求 */
	//printf("Waiting ...\n");
	
	while (printf("Waiting ...\n"))
	{/* 服务器程序多半是死循环 */
		/* 接受连接请求,从此函数中返回后就可以开始通信了 */
		int cmd_result = 0;
		//printf("Waiting ...\n");
		c_fd = accept(l_fd, (struct sockaddr*) &cin, &len);
		
		while (1)
		{
			/*
			if (recv(c_fd, buf, MAX_LINE, 0) == -1){
                                break;
                        }
			count = read(c_fd, buf, MAX_LINE);
			*/	
			/* 读取客户端传来的信息 */

			//inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p));
					/* 将客户端地址转换为字符串 */
			//printf("client IP is %s, port is %d\n", addr_p, ntohs(cin.sin_port)); 		
					/* 打印客户端地址和端口号 */
			//printf("Receive Message: %s\n", buf);			
			/* 打印客户端发送过来的字符串 */
			int wrt = 0;
			read_callback(&c_fd, buf, MAX_LINE, read);
			upper_to_lower(buf);						/* 调用大小写转换函数 */					
			cmd_result =  msg_to_cmd(buf);

			if( (cmd_result == 1 ) || connect_detect(c_fd) )           /*若客户端执行quit命令正常退出或者客户端异常断开则跳出本次循环*/
	                {
                       		printf("End communication !\n");
				break;
        	        }
			else if ( cmd_result == -1 )
			{
				char *p = "Command excuted succeed !";
				wrt = write_callback(&c_fd, p, 25, write);
			}
			else
			{
				wtr = write_callback(&c_fd, buf, strlen(buf)+1, write); /* 将转换后的字串发给客户端 */
			}
			if ( wrt == -1 )
			{
				printf("End communication !\n");
				break;
			}
		}	
		close(c_fd);			/* 通信结束,关闭套接字,准备下一次通信 */
	}
	if(close(l_fd) == -1)
	{		/* 通信结束,关闭套接字,准备下一次通信 */
		perror("Fail to close");
		exit(1);
	}
	return 0; /* 不应该执行到这里 */
}

在服务端目录~/linux/下面创建并编译helloWorld.c文件,在监听程序中由system函数执行运行helloWorld程序。

客户端程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX_LINE 100

int cmd_excute(char *cmd)
{
        printf("Excute command: %s\n",cmd);
        system(cmd);
        return 0;
}

int msg_to_cmd(char *str)
{	
	int len = strlen(str);
	if (strncasecmp(str ,"quit", len) == 0)
        {
                return 1;
        }
	else if (strncasecmp(str, "ls", len) == 0)
	{
		cmd_excute("ls ./");
		return 2;
	}	
	else if(strncasecmp(str ,"help", len) == 0)
	{
		printf("Command list:\n");
		printf("ls --display directory\n");
		printf("quit --End communication\n");
		return 3;
	}
	else
	{
	}
	return 0;
}

int main(void)
{
        struct sockaddr_in sin;
        char   msg[100];
        int    l_fd;
        char   buf[MAX_LINE];                                     /* 存储传送内容的缓冲区 */
        int    port = 80000;                                       /* 端口号,使用8000 */
	
        bzero(&sin, sizeof(sin));                       /* 清空地址结构 */
        sin.sin_family = AF_INET;                       /* 使用IPv4通信域 */
        inet_pton(AF_INET,"10.17.39.166", &sin.sin_addr);
        sin.sin_port   = htons(port);             /* 端口号转换为网络字节序 */
	l_fd           = socket(AF_INET, SOCK_STREAM, 0); /* 创立套接字,使用TCP协议 */
	connect(l_fd, (const struct sockaddr*)&sin, sizeof(sin));
	while(1)
        {
		int wrt  = -1;
		int res  = 0;
		int flag = 0;
//        	l_fd = socket(AF_INET, SOCK_STREAM, 0); /* 创立套接字,使用TCP协议 */
//	        connect(l_fd, (const struct sockaddr*)&sin, sizeof(sin));
        	printf("Input Message:\n");
        	scanf("%s", msg);
		res = msg_to_cmd(msg);
		
		switch(res){
			case 0:
				wrt = write(l_fd, msg, strlen(msg)+1);
		                sleep(3);
                		if ( wrt != -1 )
                		{
                        		printf("Write message succeed !\n");
                		}
               			else
               			{
                        		printf("Write message failed !\n");
					flag = 1;
					break;
                		}
                		read(l_fd, buf, MAX_LINE);
                		printf("Receive messaege from server:\n%s\n", buf);
				break;
			case 1:
				res = write(l_fd, msg, strlen(msg)+1);
		                sleep(3);
				close(l_fd);
				flag = 1;
				break;
			case 2:
				break;
			default:
				break;
		}
		if (flag)
		{
			break;
		}
//		printf("Socket state: %#X\n", l_fd);
	}
	if(close(l_fd) == -1){          /* 通信结束,关闭套接字,准备下一次通信 */
		perror("Fail to close !");
        }
	return 0;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章