一、用到的API
1.strtok()函数
strtok函数是用来分解字符串的,其原型是:
char *strtok(char str[], const char *delim);
其中str是要分解的字符串,delim是字符串中用来分解的字符,该函数返回分解后的字符串的起始位置指针。之所以是分解,就是说并没有生成新的字符串,只是在源字符串上面做了一些手脚,使得源字符串发生了变化,所以一定要注意,源字符串发生了变化!!!
// 例1
#include <string.h>
void main()
{
char s[] = "192.168.0.26";
char *delim = ".";
char *p;
printf("%s ", strtok(s, delim));
while((p = strtok(NULL, delim)))
printf("%s ", p);
printf("\n");
}
从例1中就可以看出strtok函数的基本使用方法,输入一个字符串数组,然后就可以将其按照一定的分隔符(例1中为".")将一个长的字符串分割成一个个短的字符串。这里需要注意的是,在对一个长字符串分割的时候,第一次调用时,strtok函数的第一个参数传入要分割的字符串,而第二次以及后面再次调用该函数的时候,strtok函数的第一个参数应该传入NULL,这是因为在strtok第一个参数为NULL的时候,该函数默认使用上一次未分割完的字符串的未分割的起始位置作为本次分割的起始位置,直到分割结束为止。
参考:君临丶天下的博客
2.strcmp()函数和strstr()函数
strcmp(s1,s2)为字符串比较函数,比较的是从字符串的第一个字符开始比较他的ASCLL码值,跟字符串的长度无关。
当s1 > s2 时,返回值 > 0
当s1 = s2 时,返回值 = 0
当s1 < s2时,返回值 < 0
! 为逻辑非运算符,即当值为真时改成假,值为假时改成真
在程序设计的判断真假中0为假,非0为真
所以在if(!srcmp(A,B))这个语句中我们可以将A和B的关系设为两种,一种是A= =B,另一种是A!=B。
当A==B时:strcmp(A,B)为0,!strcmp(A,B)为非0
if判断为真
当A!=B时:strcmp(A,B)为非0,!strcmp(A,B)为0
if判断为假
strstr函数原型:
extern char *strstr(char *str1, const char *str2);
语法:
- strstr(str1,str2)
str1: 被查找目标 string expression to search.
str2: 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
3.popen()函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
标准C库函数,函数说明:
(1)popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。
(2)参数type可使用“r”代表读取,“w”代表写入。依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。
(3)此外,所有使用文件指针(FILE*)操作的函数也都可以使用,除了fclose()以外。
(4)如果 type 为 r,那么调用进程读进 command 的标准输出。
如果 type 为 w,那么调用进程写到 command 的标准输入。
返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
**注意:**popen()会继承环境变量,通过环境变量可能会造成系统安全的问题
参考:CSDN博主「Shining-LY」
4.chdir()函数
定义和用法
chdir()函数改变当前的目录。
语法
chdir(directory ) ;
directory
。必需。
。规定新的当前目录。
返回值
。成功则返回TRUE。
。失败则返回FALSE,且抛出E_WARNING级别的错误。
5.fflush()函数
fflush是一个在C语言标准输入输出库中的函数,功能是冲洗流中的信息,该函数通常用于处理磁盘文件。fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。
概述
函数名: fflush
功 能: 清除读写缓冲区,在需要立即把输出缓冲区的数据进行物理写入时
头文件:stdio.h
原型:int fflush(FILE *stream)
其中stream是要冲洗的流
函数说明
如果指针指向一个输出流或者是一个最近的一次操作不是输入的更新流,输出刷新将会创造任意未写入的数据给将要被写入文件的流和最近的数据被修改流,并且最后的文件状态改变应该被标记为更新的基础文件的时间戳。
对于打开以使用基础文件描述进行读取的流,如果文件尚未处于EOF,并且该文件是能够搜索的文件,则基础打开文件描述的文件偏移量应设置为流的文件位置,并且任何未被从流中读取的ungetc()或ungetwc()推回到流上的字符都将被丢弃(不再进一步改变文件偏移量)。
如果stream是空指针,则fflush()将对上面定义了行为的所有流执行此刷新操作。
返回值
如果成功刷新,fflush返回0。指定的流没有缓冲区或者只读打开时也返回0值。返回EOF指出一个错误。
注意:如果fflush返回EOF,数据可能由于写错误已经丢失。当设置一个重要错误处理器时,最安全的是用setvbuf函数关闭缓冲或者使用低级I/0例程,如open、close和write来代替流I/O函数。
其他用法
fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃[非标准]
fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上。
6.access()函数
access():判断是否具有存取文件的权限
相关函数
stat,open,chmod,chown,setuid,setgid
表头文件
#include<unistd.h>
定义函数
int access(const char * pathname, int mode);
函数说明
access()会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合, R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。F_OK则是用来判断该文件是否存在。由于access()只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve()执行时则会失败。
返回值
若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1。
二、实现的功能
客户端:
1.获取服务器文件:get+文件名
2.展示服务器有哪些文件:ls
3.进入服务器某文件夹:cd+文件夹名
4.上传文件到服务器:put+文件名
5.查看客户端本地文件:lls
6.进入客户端某文件夹:lcd
三、代码展示
服务器端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "config.h"
#include <arpa/inet.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
char *getDir(char *cmsg)
{
char *p;
p=strtok(cmsg," ");
p=strtok(NULL," ");
return p;
}
int cmd_deal(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(strstr(cmd,"cd")) return CD;
if(strstr(cmd,"get")) return GET;
if(strstr(cmd,"put")) return PUT;
return 100;
}
void data_Handler(int fd,struct Msg msg)
{
int ret;
int filefd;
char readBuf[1024]={0};
char *dir;
char *file;
FILE *fp;
printf("cmd:%s\n",msg.data);
ret=cmd_deal(msg.data);
switch(ret){
case LS:
case PWD:
fp=popen(msg.data,"r");
fread(msg.data,sizeof(msg.data),1,fp);
write(fd,&msg,sizeof(msg));
break;
case CD:
dir=getDir(msg.data);
printf("%s\n",dir);
chdir(dir);
break;
case GET:
file=getDir(msg.data);
if(access(file,F_OK)==-1){
strcpy(msg.data,"no this file");
write(fd,&msg,sizeof(msg));
break;
}else{
printf("file exists\n");
if(filefd=open(file,O_RDWR)<0){
printf("open failed\n");
}else{
read(filefd,&readBuf,sizeof(readBuf));
strcpy(msg.data,readBuf);
write(filefd,&msg,sizeof(msg));
}
break;
}
case PUT:
filefd=open(getdir(msmg.data),O_RDWR|O_CREAT,0666);
write(fd,msg.secondsbuf,sizeof(msg.secondsbuf));
close(filefd);
break;
case QUIT:
printf("client out\n");
exit(-1);
}
}
int main(int argc,char **argv)
{
int s_fd;
int c_fd;
int n_read;
struct Msg msg;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
s_fd=socket(AF_INET,SOCK_STREAM,0);
if(s_fd==-1){
printf("create failed\n");
perror("scoket");
}
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
if(bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1){
perror("bind");
exit(-1);
}
if(listen(s_fd,10)==-1){
perror("listen");
exit(-1);
}
int clen=sizeof(struct sockaddr_in);
printf("wait connect...\n");
while(1){
c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd==-1){
printf("accept failed\n");
perror("accept");
exit(-1);
}
if(fork()==0){
printf("client success connect\n");
while(1){
memset(&msg.data,0,sizeof(msg.data));
n_read=read(c_fd,&msg,sizeof(msg));
if(n_read==0){
printf("client out\n");
}else if(n_read>0){
data_Handler(c_fd,msg);
}
}
}
}
return 0;
}
客户端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "config.h"
#include <arpa/inet.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
char *getDir(char *cmd)
{
char *p;
p=strtok(cmd," ");
p=strtok(NULL," ");
return p;
}
int cmd_deal(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("lls",cmd)) return LLS;
if(strstr(cmd,"lcd")) return LCD;
if(strstr(cmd,"cd")) return CD;
if(strstr(cmd,"get")) return GET;
if(strstr(cmd,"put")) return PUT;
return 100;
}
int data_Handler(int fd,struct Msg msg)
{
int ret;
int filefd;
char *file;
char readBuf[1024]={0};
FILE *fp;
printf("cmd:%s\n",msg.data);
ret=cmd_deal(msg.data);
switch(ret){
case LS:
case PWD:
case CD:
write(fd,&msg,sizeof(msg));
break;
case GET:
write(fd,&msg,sizeof(msg));
break;
case PUT:
strcpy(readBuf,msg.data);
file=getDir(readBuf);
if(filefd=open(file,O_RDWR)<0){
printf("open failed\n");
exit(-1);
}
if(access(file,"F_OK")==-1){
printf("file no exists\n");
}else{
read(filefd,msg.secondsbuf,sizeof(msg.secondsbuf));
close(filefd);
write(fd,&msg,sizeof(msg));
}
break;
case LLS:
system("ls");
break;
case LCD:
file=getDir(msg.data);
chdir(file);
break;
case QUIT:
printf("quit\n");
exit(-1);
}
return ret;
}
void message_deal(int fd,struct Msg msg)
{
struct Msg msggest;
read(fd,&msggest,sizeof(msggest));
printf("%s\n",msggest.data);
putchar('>');
fflush(stdout);
}
int main(int argc,char **argv)
{
int c_fd;
int n_read;
int ret;
struct Msg msg;
struct sockaddr_in c_addr;
c_fd=socket(AF_INET,SOCK_STREAM,0);
if(c_fd==-1){
printf("create failed\n");
perror("scoket");
}
c_addr.sin_family=AF_INET;
c_addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
printf("connect...\n");
connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));
while(1){
printf("input:\n");
gets(msg.data);
ret=data_Handler(c_fd,msg);
if(ret>IGFO){
putchar('>');
fflush(stdout);
continue;
}
if(ret==-1){
printf("no command\n");
putchar('>');
fflush(stdout);
continue;
}
message_deal(c_fd,msg);
}
return 0;
}
自建头文件config.h
#define LS 0
#define PWD 1
#define QUIT 2
#define IGFO 3
#define CD 4
#define PUT 5
#define GET 6
#define LLS 7
#define LCD 8
#define DOFINE 9
struct Msg
{
char data[1024];
char secondsbuf[128];
};
~
四、小结
实现原理就是通过socket套接字创建连接,实现数据的收发。包含很多Linux编程的知识,可作为Linux学习的总结。