本文章是前段时间学习linux编程的一个小总结,如果不总结的话我怕我学完就忘emmm(其实已经忘的差不多了),本文是跟着上官可编程老师与朱有鹏老师学习之后的练手作品,本人学疏才浅,暂时只能到这个地步
提示:以下是本篇文章正文内容
利用socket,建立起服务端与客户端的对接;(服务端能支持多台客户端的同时连接)
1、客户端输入ls指令,能获取服务端上文件列表。
2、客户端输入cd指令+路径,可以切换服务端的目录。
3、在程序运行的过程中,客户端输入lls指令 能够查看自己的文件列表
4、在程序运行的过程中,客户端输入lcd指令+路径 能够切换自己的目录。
5、客户端输入get指令+文件名,能将服务端上面的某个文件下载到客户端
6、客户端输入put指令+文件名,能将客户端上面的某个文件上传到服务端
1、不断监听客户端的指令(等待指令)。
2、在接收上面客户端的指令后,去执行指令。
(1)服务端建立socket连接:socket—>blind—>listen—>accept
(2)客户端建立socket连接:socket—>connect
(1)定义指令
(2)客户端匹配用户输入的指令,客户端发送指令到服务端,服务端解析指令作出响应
服务端建立socket连接
流程:socket—>blind—>listen—>accept
代码如下(示例):
int main(int argc,char **argv)
{int c_fd;int s_fd;int n_read;char readBuf[128];struct Msg msg;struct sockaddr_in s_addr;struct sockaddr_in c_addr;/*调试信息,这里我想要用argv,argc,进行手动指定端口号和IP地址,所以在用这两个参数之前需要先判断*/if(argc != 3){printf("parameter error!n");exit(-1);}/*用之前清空,防止数据产生混乱*/memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socket/*s_fd:这里socket返回的是监听fd,是用来监听客户端的,不能用来和任何客户端进行读写*//*AF_INET:IPv4,SOCK_STREAM:tcp协议,0:使用系统默认的方式*/s_fd = socket(AF_INET,SOCK_STREAM,0);/*如果返回值为-1说明错误,打印出错误号*/if(s_fd == -1){perror("socket");exit(-1);}//2.bind/*有IPv4和IPv6两种,这里我们选IPv4*/s_addr.sin_family = AF_INET; // 设置地址族为IPv4/*htons:端口号大小端的转换(具体大小端要看对应的计算机)atoi:字符串转换成整型,使我们输入的数字端口号可以被识别*/s_addr.sin_port = htons(atoi(argv[2])); // 设置地址的端口号信息/*inet_aton:将我们输入的地址字符串转换成网络可以识别的api*/inet_aton(argv[1],&s_addr.sin_addr); // 设置IP地址/*(struct sockaddr *)&s_addr这之所以需要类型转换的原因是因为我们用的是IPV4的结构体,而给出的标准是IPV4和IPV6的通用结构体,所以我们一般用IPV4的结构体之后将其转换为系统给出的通用结构体,其实转不转都一样,就是会报警告而已,而这个警告是我们可以理解的警告,所以问题不大*/bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen/*设置最大监听10个*/listen(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);/*while不停的循环接收客户端发来的信息,并且作出响应*/while(1){/*accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写。*/c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);/*调试信息*/if(c_fd != -1){perror("accept");}
/*成功打印出连接的地址*/printf("get connect: %sn",inet_ntoa(c_addr.sin_addr));
/*当我的accept每接收到一个信息就建立一个子进程*/if(fork() == 0){while(1){/*每次循环接收信息之前,清空之前的信息*/d,0,d));/*用accept建立的连接fd来读写信息*/n_read = read(c_fd,&msg,sizeof(msg));if(n_read == 0){printf("client outn");break;}else if(n_read > 0){/*一旦发现有信息,就执行这个函数*/msg_handler(msg,c_fd);}}}}
/*关闭监听fd和连接fd*/close(c_fd);close(s_fd);return 0;
}
客户端建立socket连接
流程:socket—>connect
客户端建立socket与服务端建立socket大同小异这里就不过多赘述了
代码如下(示例):
int main(int argc,char **argv)
{int c_fd;struct Msg msg;struct sockaddr_in c_addr;if(argc != 3){printf("parameter error!n");exit(-1);}memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}c_addr.sin_family = AF_INET;c_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&c_addr.sin_addr);//2.connectif(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){perror("connect");exit(-1);}printf("connect ...n");return 0;}
定义指令
共有指令
代码如下(示例):
#define LS 0
#define PWD 1
#define GET 2#define IFGO 3#define CD 4
#define PUT 5
#define LLS 6
#define LCD 7
#define LPWD 8#define QUIT 9
#define DOFILE 10struct Msg
{int type;char cmd[1024];char buf[128];};
服务端指令
代码如下(示例):
int get_cmd_type(char *cmd) //检测命令并转为相应int
{if(!strcmp("ls",cmd)) return LS;//strcmp,比较的字符串相等时返回0if(!strcmp("pwd",cmd)) return PWD;if(!strcmp("quit",cmd)) return QUIT;if(strstr(cmd,"cd")) return CD;//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串if(strstr(cmd,"get")) return GET;if(strstr(cmd,"put")) return PUT;return 10;}
客户端指令
代码如下(示例):
int get_cmd_type(char *cmd)
{if(strstr(cmd,"lcd")) return LCD;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;if(!strcmp("lls",cmd)) return LLS;return -1;}
客户端匹配用户输入的指令,客户端发送指令到服务端,服务端解析指令作出响应
客户端:
代码如下(示例):
/*这是一个分割命令的函数,为了实现cd指令+路径*/
char *getDir(char *cmd)
{char *p;p = strtok(cmd," ");p = strtok(NULL," ");return p;}
/*当客处理用户输入指令函数*/
int cmd_handler(struct Msg msg,int fd)
{char *dir = NULL;char buf[32];int ret;int filefd;ret = get_cmd_d); //cmd转为int类型
/*根据不同的指令,进行不同的操作*/switch(ret){case LS:case CD:case PWD:case pe = 0;write(fd,&msg,sizeof(msg));break;case PUT:strcpy(d);dir = getDir(buf); //获取第二个参数if(access(dir,F_OK) == -1){ //判断文件是否存在printf("%s not exsitn",dir);}else{filefd = open(dir,O_RDWR);read(filefd,msg.buf,sizeof(msg.buf)); //读文件内容close(filefd);write(fd,&msg,sizeof(msg));}break;case LPWD:printf("-------------------------------------nn");system("pwd");printf("n-------------------------------------n");break;case LLS:printf("-------------------------------------nn");system("ls");printf("n-------------------------------------n");break;case LCD:dir = d); //获取第二个参数int t = chdir(dir); //cdbreak;case QUIT:d,"quit");write(fd,&msg,sizeof(msg));close(fd);exit(-1);}return ret;}
//处理服务端发来的指令
void handler_server_message(int c_fd,struct Msg msg)
{int n_read;struct Msg msgget;int newfilefd;n_read = read(c_fd,&msgget,sizeof(msgget));if(n_read == 0){printf("server is out,quitn");exit(-1);}else pe == DOFILE){char *p = d);newfilefd = open(p,O_RDWR|O_CREAT,0600);write(d,d));putchar('>');fflush(stdout);}else{printf("-------------------------------------n");printf("n%sn",d);printf("-------------------------------------n");putchar('>');fflush(stdout);}}
服务端
代码如下(示例):
/*解析客户端发来的指令,并对应作出响应*/
void msg_handler(struct Msg msg,int fd)
{char dataBuf[1024] = {0};char *file = NULL;int fdfile;printf("cmd: %sn",d); //打印命令int ret = get_cmd_d); //将命令转为int类型switch(ret){case LS:case pe = 0;FILE *r = d,"r"); //执行命令,返回结果d,d),1,r); //将结果读到dwrite(fd,&msg,sizeof(msg)); //写入客户端break;case pe = 1;char *dir = d); //获取第二个参数printf("dir:%sn",dir);chdir(dir); //系统调用函数,同cdbreak;case GET:file = d);if(access(file,F_OK) == -1){ //判断文件是否存在d,"Have Not File!");write(fd,&msg,sizeof(msg));}else{pe = DOFILE; //设置标志fdfile = open(file,O_RDWR);read(fdfile,dataBuf,sizeof(dataBuf)); //读文件内容close(fdfile);d,dataBuf);write(fd,&msg,sizeof(msg));}break;case PUT:fdfile = open(d),O_RDWR|O_CREAT,0600);write(fdfile,msg.buf,strlen(msg.buf));close(fdfile);break;case QUIT:printf("client quit!n");exit(-1);}}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include <unistd.h>int get_cmd_type(char *cmd)
{if(strstr(cmd,"lcd")) return LCD;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;if(!strcmp("lls",cmd)) return LLS;return -1;}
/*==============以上是定义指令=====================*/
char *getDir(char *cmd)
{char *p;p = strtok(cmd," ");p = strtok(NULL," ");return p;}int cmd_handler(struct Msg msg,int fd)
{char *dir = NULL;char buf[32];int ret;int filefd;ret = get_cmd_d); //cmd转为int类型switch(ret){case LS:case CD:case PWD:case pe = 0;write(fd,&msg,sizeof(msg));break;case PUT:strcpy(d);dir = getDir(buf); //获取第二个参数if(access(dir,F_OK) == -1){ //判断文件是否存在printf("%s not exsitn",dir);}else{filefd = open(dir,O_RDWR);read(filefd,msg.buf,sizeof(msg.buf)); //读文件内容close(filefd);write(fd,&msg,sizeof(msg));}break;case LPWD:printf("-------------------------------------nn");system("pwd");printf("n-------------------------------------n");break;case LLS:printf("-------------------------------------nn");system("ls");printf("n-------------------------------------n");break;case LCD:dir = d); //获取第二个参数int t = chdir(dir); //cdbreak;case QUIT:d,"quit");write(fd,&msg,sizeof(msg));close(fd);exit(-1);}return ret;}
/*==============以上是客户端匹配用户输入的指令=====================*/
void handler_server_message(int c_fd,struct Msg msg)
{int n_read;struct Msg msgget;int newfilefd;n_read = read(c_fd,&msgget,sizeof(msgget));if(n_read == 0){printf("server is out,quitn");exit(-1);}else pe == DOFILE){char *p = d);newfilefd = open(p,O_RDWR|O_CREAT,0600);write(d,d));putchar('>');fflush(stdout);}else{printf("-------------------------------------n");printf("n%sn",d);printf("-------------------------------------n");putchar('>');fflush(stdout);}}
/*==============以上是处理服务端发来的消息=====================*/
/*==============以下是建立socket连接=====================*/
int main(int argc,char **argv)
{int c_fd;struct Msg msg;struct sockaddr_in c_addr;if(argc != 3){printf("parameter error!n");exit(-1);}memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}c_addr.sin_family = AF_INET;c_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&c_addr.sin_addr);//2.connectif(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){perror("connect");exit(-1);}printf("connect ...n");int mark = 0;while(1){d,0,d));if(mark == 0) printf(">");d);if(d) == 0){if(mark == 1){printf(">");}continue;}mark = 1;int ret = cmd_handler(msg,c_fd);if(ret > IFGO){putchar('>');fflush(stdout); //打印输出缓冲区到标准输出设备上continue;}if(ret == -1){printf("command not n");printf(">");fflush(stdout);continue;}handler_server_message(c_fd,msg);}return 0;}
代码如下(示例):
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include <unistd.h>int get_cmd_type(char *cmd) //检测命令并转为相应int
{if(!strcmp("ls",cmd)) return LS;//strcmp,比较的字符串相等时返回0if(!strcmp("pwd",cmd)) return PWD;if(!strcmp("quit",cmd)) return QUIT;if(strstr(cmd,"cd")) return CD;//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串if(strstr(cmd,"get")) return GET;if(strstr(cmd,"put")) return PUT;return 10;}
/*==============以上是定义指令=====================*/
char *getDesDir(char *cmsg) //取空格分开的的第二个字符串
{char *p;p = strtok(cmsg," ");//char *strtok(char s[], const char *delim);分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。p = strtok(NULL," ");return p;
}void msg_handler(struct Msg msg,int fd)
{char dataBuf[1024] = {0};char *file = NULL;int fdfile;printf("cmd: %sn",d); //打印命令int ret = get_cmd_d); //将命令转为int类型switch(ret){case LS:case pe = 0;FILE *r = d,"r"); //执行命令,返回结果d,d),1,r); //将结果读到dwrite(fd,&msg,sizeof(msg)); //写入客户端break;case pe = 1;char *dir = d); //获取第二个参数printf("dir:%sn",dir);chdir(dir); //系统调用函数,同cdbreak;case GET:file = d);if(access(file,F_OK) == -1){ //判断文件是否存在d,"Have Not File!");write(fd,&msg,sizeof(msg));}else{pe = DOFILE; //设置标志fdfile = open(file,O_RDWR);read(fdfile,dataBuf,sizeof(dataBuf)); //读文件内容close(fdfile);d,dataBuf);write(fd,&msg,sizeof(msg));}break;case PUT:fdfile = open(d),O_RDWR|O_CREAT,0600);write(fdfile,msg.buf,strlen(msg.buf));close(fdfile);break;case QUIT:printf("client quit!n");exit(-1);}}
/*==============以上是处理客户端发来的消息=====================*/
/*==============以下是建立socket连接=====================*/
int main(int argc,char **argv)
{int c_fd;int s_fd;int n_read;char readBuf[128];struct Msg msg;struct sockaddr_in s_addr;struct sockaddr_in c_addr;if(argc != 3){printf("parameter error!n");exit(-1);}memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1.sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&s_addr.sin_addr);//2.bindbind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if(c_fd != -1){perror("accept");}printf("get connect: %sn",inet_ntoa(c_addr.sin_addr));if(fork() == 0){while(1){d,0,d));n_read = read(c_fd,&msg,sizeof(msg));if(n_read == 0){printf("client outn");break;}else if(n_read > 0){msg_handler(msg,c_fd);}}}}close(c_fd);close(s_fd);return 0;
}
代码如下(示例):
#define LS 0
#define PWD 1
#define GET 2#define IFGO 3#define CD 4
#define PUT 5
#define LLS 6
#define LCD 7
#define LPWD 8#define QUIT 9
#define DOFILE 10struct Msg
{int type;char cmd[1024];char buf[128];};
1.socket返回的fd叫做监听fd,是用来监听客户端的,不能用来和任何客户端进行读写;accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写
2.(struct sockaddr *)&s_addr这之所以需要类型转换的原因是因为我们用的是IPV4的结构体,而给出的标准是IPV4和IPV6的通用结构体,所以我们一般用IPV4的结构体之后将其转换为系统给出的通用结构体
3. htons:端口号大小端的转换(具体大小端要看对应的计算机)
atoi:字符串转换成整型,使我们输入的数字端口号可以被识别
inet_aton:将我们输入的地址字符串转换成网络可以识别的api
除了上面用到的,我们常用到的一些函数
inet_addr、inet_ntoa、inet_aton
inet_pton、inet_ntop
4.记得在每次数据交换的时候都要清空数据,不然会有上一次的数据进行干扰
在
那么这个小练习到这里就结束了,里面用到了多进程,socket相关的一些知识,而且虽然是个小练习但是整个框架就是一个项目的缩影,对于刚接触linux编程的同学来说联系一下还是有很大的好处的
本文发布于:2024-01-29 16:19:17,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170651635816543.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |