Linux系统编程之网络编程项目(Socket编程)

程序介绍

该程序是建立在局域网下,模拟服务器与客户端之间通信的方式,可供选择的功能包括接收文件、显示当前路径、更换路径等。如果使用虚拟机的话,请使用桥接模式进行网络配置,相关配置方法如链接所示:VMware虚拟机网络配置—桥接模式

演示步骤

1.查询网络ip

本文以虚拟机进行代替,严谨地说,应该采用局域网下的两个不同的电脑端进行收发信息,但只是为了解释相关效果以及程序,所以方便起见,以虚拟机不同路径下进行测试。
首先在服务端的Linux系统上输入: ifconfig
查询服务端的ip地址,即192.168.175.128。

lamda@lamda-virtual-machine:~/Desktop$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.175.128  netmask 255.255.255.0  broadcast 192.168.175.255
        inet6 fe80::20c:29ff:fe39:171c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:39:17:1c  txqueuelen 1000  (Ethernet)
        RX packets 157250  bytes 218860850 (218.8 MB)
        RX errors 1429  dropped 33  overruns 0  frame 0
        TX packets 66801  bytes 3858581 (3.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 19  base 0x2000  

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 3473  bytes 316259 (316.2 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3473  bytes 316259 (316.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

2.启动服务端和客户端,展示效果

服务端:

lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ ls
command.h  demo_server.c  kehu  server
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ gcc demo_server.c -o server
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ ./server 192.168.175.128 8983     //创建网络地址等待连接,一般选择服务端ip地址
connect: NO.1 client ip_addr = 192.168.175.128   //获取客户端ip地址

客户端:

lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ ls
client  demo_client.c  demo_mutex.c
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ gcc demo_client.c -o client
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ ./client 192.168.175.128 8983
=========================================
> help
The included commands are as follows:
ls-----list directory contents of current path from server.
lls----list directory contents of current path from client.
pwd----print name of current directory from server.
get----get file from server.
cd-----change the working directory.
quit---quit this program.
=========================================
> ls
command.h
demo_server.c
kehu
server
=========================================
> lls
client	demo_client.c  demo_mutex.c
=========================================
> pwd
/home/lamda/Desktop/code/Linux/Socket/ftp_make
=========================================
> cd ..   
=========================================
> ls
client
demo_client.c
demo_server.c
ftp_make
server
=========================================
> cd ../Thread
=========================================
> ls
a.out
demo_cond.c
demo_mutex.c
demo_thread.c
=========================================
> get demo_cond.c
=========================================
> quit
Program is over.

3.相关代码

服务端

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "command.h"
#include <sys/stat.h>
#include <fcntl.h>
char *take(char *readbuf) {
	char *str;
	str = strtok(readbuf," ");
	str = strtok(NULL," ");
	return str;
}
void handle(int num,char *readbuf,char *sendbuf) {
        FILE *fps;
	char *str;
        int fd;
	switch (num) {
		case LS:
			fps = popen(readbuf,"r");
			fread(sendbuf,sizeof(char),1024,fps);
			pclose(fps);
			break;
		case PATH:
			fps = popen(readbuf,"r");
			fread(sendbuf,sizeof(char),1024,fps);
			pclose(fps);
			break;
		case CD:
			
			str = take(readbuf);
			chdir(str);
			strcpy(sendbuf," ");
			break;
		case GET:
			
			str = take(readbuf);
			fd = open(str,O_RDWR);
			if(fd==-1){
				printf("This file maybe not exist.\n");
			}
			else{
				int num_seek = lseek(fd,0,SEEK_END);
				printf("num_seek = %d\n",num_seek);
				lseek(fd,0,SEEK_SET);
				read(fd,sendbuf,num_seek);

			}
			close(fd);
			break;
		case PUT:
			strcpy(sendbuf," ");
			break;
		case HELP:
			strcpy(sendbuf," ");
			break;
		case QUIT:
			printf("Program is over.\n");
			strcpy(sendbuf," ");
			break;
		default:
			strcpy(sendbuf,"command has not been defined");
			break;
	}
}


int analysis(char *readbuf) {
	int com_num;
	if(strcmp(readbuf,"ls")==0) com_num = LS;
	else if(strcmp(readbuf,"lls")==0) com_num = LLS; 
	else if(strcmp(readbuf,"pwd")==0) com_num = PATH;
	else if(strcmp(readbuf,"quit")==0) com_num = QUIT;
	else if(strcmp(readbuf,"help")==0) com_num = HELP;
	else if(strstr(readbuf,"get")!=NULL) com_num = GET;
	else if(strstr(readbuf,"put")!=NULL) com_num = PUT;
	else if(strstr(readbuf,"cd")!=NULL) com_num = CD;
	else {
		com_num = -1;
	}
	return com_num;
}

int main(int argc,char **argv) {
	if(argc != 3) {
		printf("prarmeter num is wrong.\n");
		exit(-1);
	}
	// socket  int socket(int domain, int type, int protocol);

	int socket_fd = socket(AF_INET,SOCK_STREAM,0);

	if(socket_fd == -1) {
		perror("Socket");
		exit(-1);
	}

	// bind   int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

	struct sockaddr_in my_addr,client_addr;
	memset(&my_addr,0,sizeof(struct sockaddr_in));
	memset(&client_addr,0,sizeof(struct sockaddr_in));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&my_addr.sin_addr);
	int bind_check = bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
	if(bind_check ==-1){
		perror("Bind");
		exit(-1);
	}
	// listen int listen(int sockfd, int backlog);
	listen(socket_fd,10);
	int client_num = 0;
	// accept  int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	//int connect_fd = accept(socket_fd,NULL,NULL);
	unsigned int addrlen = sizeof(struct sockaddr_in); 
	int client_fd = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
	if(client_fd ==-1)
	{
		perror("accept");
		exit(-1);
	}
	client_num++;
	printf("connect: NO.%d client ip_addr = %s\n",client_num,inet_ntoa(client_addr.sin_addr));
	char readbuf[128]={0};
	char sendbuf[102400]={0};
	int flags = 1;
	if(vfork()==0){
		// read
		while(flags) {
			memset(readbuf,0,sizeof(readbuf));
			int n_read = read(client_fd,readbuf,sizeof(readbuf));
			if(n_read == -1){
				perror("Read");
				exit(-1);
			}
			printf("%s\n",readbuf);
			memset(sendbuf,0,sizeof(sendbuf));
			int num = analysis(readbuf);
			if(num == QUIT)
				flags = 0;
			handle(num,readbuf,sendbuf);
			int n_write = write(client_fd,sendbuf,strlen(sendbuf));

		}
		exit(3);
	}
	// close
	close(socket_fd);
	return 0;
}

command.h //用来解析命令,给命令赋值

#ifndef COMMAND_H_
#define COMMAND_H_

#define LS   0
#define LLS  1
#define PATH 2
#define QUIT 3
#define HELP 4
#define LINE 5
#define GET  6
#define PUT  7
#define CD   8

#endif

客户端

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

void check(char * argv) {
	int size = strlen(argv);
	argv[size-1] = '\0';
}
char *take(char *readbuf) {
	char *str;
	str = strtok(readbuf," ");
	str = strtok(NULL," ");
	return str;
}

int main(int argc,char **argv) {
	if(argc != 3) {
		printf("prarmeter num is wrong.\n");
		exit(-1);
	}
	// socket  int socket(int domain, int type, int protocol);

	int socket_fd = socket(AF_INET,SOCK_STREAM,0);

	if(socket_fd == -1) {
		perror("Socket");
		exit(-1);
	}
	// connect  int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	struct sockaddr_in my_addr;
	memset(&my_addr,0,sizeof(struct sockaddr_in));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&my_addr.sin_addr);
	int connect_fd = connect(socket_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
	if(connect_fd ==-1){
		perror("Connect");
		exit(-1);
	}
	int flags = 1;
	char sendbuf[128] = {0}; 
	char readbuf[102400] = {0};
	if(vfork()==0){
		while(flags){
			memset(sendbuf,0,sizeof(sendbuf));
			printf("=========================================\n");
			printf("> ");
			fgets(sendbuf,sizeof(sendbuf),stdin);
			check(sendbuf);
			//printf("You send : %s\n",sendbuf);
			if(strcmp(sendbuf,"quit")==0) {
				flags = 0;
				printf("Program is over.\n");
			}
			else if(strcmp(sendbuf,"lls")==0) 
				system("ls");
			else if(strcmp(sendbuf,"help")==0) {
				printf("The included commands are as follows:\n");
				printf("ls-----list directory contents of current path from server.\n");
				printf("lls----list directory contents of current path from client.\n");
				printf("pwd----print name of current directory from server.\n");
				printf("get----get file from server.\n");
				printf("cd-----change the working directory.\n");
				printf("quit---quit this program.\n");
			}
			else if(strstr(sendbuf,"get")!=NULL) {
				int n_write = write(socket_fd,sendbuf,strlen(sendbuf));
				memset(readbuf,0,sizeof(readbuf));
				int n_read = read(socket_fd,readbuf,sizeof(readbuf));
				if(n_read == -1){
				        perror("Read");
					exit(-1);
				}
				FILE *fp = fopen(take(sendbuf),"a+");
                                if(fp ==NULL)
				{
					perror("get file");
					continue;
				}
                               fwrite(readbuf,sizeof(char),strlen(readbuf),fp);
				fclose(fp);
				
			}
			else{
				int n_write = write(socket_fd,sendbuf,strlen(sendbuf));
				memset(readbuf,0,sizeof(readbuf));
				int n_read = read(socket_fd,readbuf,sizeof(readbuf));
				if(n_read == -1){
                   			perror("Read");
		        		exit(-1);
				}
				puts(readbuf);
			}
		}
		exit(3);
	}
	// close
	close(socket_fd);
	return 0;
}

4.代码使用函数介绍

(1)服务端开启后,需要双方一直保持收发消息直至程序退出,所以当连接成功后,客户端和服务端各自开启一个子进程进行对接,客户端发送消息后,还要准备接收消息,服务端负责接收消息并给出相应操作;在这里必须保证子进程运行期间父进程不能抢占资源,而是让子进程一直在等待接收消息,所以使用vfork函数,也可以用fork函数搭配wait使用。
(2)当采用cd,get等命令时,需要截取字符串空格后的内容,所以采用strtok函数,具体使用方法及原理可参照:C 库函数 - strtok()
(3)函数命令解析用到command.h文件,所以服务端必须与该文件在同一目录下进行编译。
(4)此项目还可以添加其他命令,例如实现vi命令,可分若干步进行操作:1.客户端接收文件2.调用系统命令,在客户端本地进行vi操作3.传输文件到服务端,目前仍在尝试加入。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值