要求:

写一个客户端程序和服务器程序,客户端程序连接上服务器之后,通过敲命令和服务器进行交互,支持的交互命令包括:

  • pwd:显示服务器应用程序启动时的当前路径。
  • cd:改变服务器应用程序的当前路径。
  • ls:显示服务器应用程序当前路径下的文件列表。
  • quit:客户端进程退出,但是服务器端不能退出,第二个客户可以再次连接上服务器端。

客户端向服务端发送交互命令,服务端将查询的结果返回给客户端

开干

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h> #define MAXLINE 1024 int tcp_client(char *address, int port)
{
int socket_fd;
socket_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_pton(AF_INET, address, &server_addr.sin_addr); socklen_t server_len = sizeof(server_addr);
int connect_rt = connect(socket_fd, (struct sockaddr *)&server_addr, server_len);
if(connect_rt < 0)
{
perror("connect failed");
return -1;
} return socket_fd;
} int main(int argc, char *argv[])
{
if(argc != 3)
{
perror("usage: cmd_client <IPaddress> <port>");
return -1;
} int port = atoi(argv[2]);
printf("argv1:%s\n",argv[1]);
int socket_fd = tcp_client(argv[1], port); char recv_line[MAXLINE],send_line[MAXLINE];
int n; fd_set readmask;
fd_set allreads;
FD_ZERO(&allreads);
FD_SET(0, &allreads);
FD_SET(socket_fd, &allreads); for(;;)
{
readmask = allreads;
int rc = select(socket_fd+1, &readmask, NULL, NULL, NULL);
if(rc <= 0)
{
perror("select failed");
return -1;
} if(FD_ISSET(socket_fd, &readmask))
{
n = read(socket_fd, recv_line, MAXLINE);
if(n < 0)
{
perror("read error");
return -1;
}
else if(n == 0)
{
printf("server closed");
break;
} recv_line[n] = 0;
fputs(recv_line, stdout);
fputs("\n",stdout);
} if(FD_ISSET(STDIN_FILENO, &readmask))
{
if(fgets(send_line, MAXLINE, stdin) != NULL)
{
int i = strlen(send_line);
if(send_line[i - 1] == '\n')
{
send_line[i - 1] = 0;
} if(strncmp(send_line, "quit", strlen(send_line)) == 0)
{
if(shutdown(socket_fd, 1))
{
perror("shutdown failed");
return -1;
}
} size_t rt = write(socket_fd, send_line, strlen(send_line));
if(rt < 0)
{
perror("write failed");
return -1;
} }
} } exit(0);
}

客户端的代码主要考虑的是使用 select 同时处理标准输入和套接字。select 如果发现标准输入有事件,读出标准输入的字符,就会通过调用 write 方法发送出去。如果发现输入的是 quit,则调用 shutdown 方法关闭连接的一端。

如果 select 发现套接字流有可读事件,则从套接字中读出数据,并把数据打印到标准输出上;如果读到了 EOF,表示该客户端需要退出,直接退出循环,通过调用 exit 来完成进程的退出。

服务端程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h> #define SERV_PORT 43211
#define LISTENQ 1024 static int count; static void sig_int(int signo) {
printf("\nreceived %d datagrams\n", count);
exit(0);
} char *run_cmd(char *cmd)
{
char *data = malloc(16384);
bzero(data, sizeof(data));
FILE *fdp;
const int max_buffer = 256;
char buffer[max_buffer];
fdp = popen(cmd, "r");
char *data_index = data; if (fdp)
{
while(!feof(fdp))
{
if(fgets(buffer, max_buffer, fdp) != NULL)
{
int len = strlen(buffer);
memcpy(data_index, buffer, len);
data_index += len;
}
}
pclose(fdp);
}
return data; } int main(int argc, char *argv[])
{
int listenfd;
listenfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERV_PORT); int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); int rt1 = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if(rt1 < 0)
{
perror("bind failed");
return -1;
} int rt2 = listen(listenfd, LISTENQ);
if(rt2 < 0)
{
perror("listen failed");
return -1;
} signal(SIGPIPE, SIG_IGN); int connfd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr); char buf[256];
count = 0;
while(1)
{
if((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len)) < 0)
{
perror("accept failed");
return -1;
} while(1)
{
bzero(buf, sizeof(buf));
int n = read(connfd, buf, sizeof(buf));
if(n < 0)
{
perror("error read message");
continue;
}
else if(n == 0)
{
printf("client closed ");
close(connfd);
break;
} count++;
buf[n] = 0;
if(strncmp(buf, "ls", n) == 0)
{
char *result = run_cmd("ls");
if(send(connfd, result, strlen(result), 0) < 0)
{
return 1;
}
free(result);
}
else if(strncmp(buf, "pwd", n) == 0)
{
char buf[256];
char *result = getcwd(buf, 256);
if(send(connfd, result, strlen(result), 0) < 0)
{
return 1;
}
}
else if(strncmp(buf, "cd ", 3) == 0)
{
char target[256];
bzero(target, sizeof(target));
memcpy(target, buf+3, strlen(buf) - 3);
if(chdir(target) == -1)
{
printf("change dir failed, %s\n",target);
}
}
else
{
char *error = "error: unknow input type";
if(send(connfd, errno, strlen(errno), 0) < 0)
{
return 1;
}
}
} }
exit(0); }

服务器端程序需要两层循环,第一层循环控制多个客户端连接,第二层循环控制和单个连接的数据交互

在第一层循环里通过 accept 完成了连接的建立,获得连接套接字。在第二层循环里,先通过调用 read 函数从套接字获取字节流。这里处理的方式是反复使用了 buf 缓冲,每次使用之前记得都要调用 bzero 完成初始化,以便重复利用。如果读取数据为 0,则说明客户端尝试关闭连接,这种情况下,需要跳出第二层循环,进入 accept 阻塞调用,等待新的客户连接到来

在读出客户端的命令之后,就进入处理环节。通过字符串比较命令,进入不同的处理分支。C 语言的 strcmp 或者 strncmp 可以帮助我们进行字符串比较,

如果命令的格式有错,需要把错误信息通过套接字传给客户端。

对于“pwd”命令,通过调用 getcwd 来完成的,getcwd 是一个 C 语言的 API,可以获得当前的路径。

对于“cd”命令,通过调用 chdir 来完成的,cd 是一个 C 语言的 API,可以将当前目录切换到指定的路径。

对于“ls”命令,通过了 popen 的方法,执行了 ls 的 bash 命令,把 bash 命令的结果通过文件字节流的方式读出,再将该字节流通过套接字传给客户端。

运行结果

网络编程:CMD命令的更多相关文章

  1. windows查看网络常用cmd命令

    一.ping 主要是测试本机TCP/IP协议配置正确性与当前网络现状.  ping命令的基本使用格式是: ping IP地址/主机名/域名 [-t] [-a] [-n count] [-l size] ...

  2. C# 网络与Cmd命令

    网络命令行: 1 - ping 2 - ipconfig 本机网络配置情况 3 - net 4 - arp  网络网卡物理/ip地址对应用 5 - tracert 列举数据报到达目标地所经过的网关 6 ...

  3. 03.windows系统重新分配ip的cmd命令

    网络重启CMD命令 ipconfig /release  --- 释放ip搜索 ipconfig /renew --- 重新获得

  4. Python网络编程03 /缓存区、基于TCP的socket循环通信、执行远程命令、socketserver通信

    Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命令.socketserver通信 目录 Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命 ...

  5. Windows最常用的几个网络CMD命令总结

    Windows最常用的几个网络CMD命令总结 http://www.cnblogs.com/sbaicl/archive/2013/03/05/2944001.html 一.ping 主要是测试本机T ...

  6. 脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?

    本文引用了公众号纯洁的微笑作者奎哥的技术文章,感谢原作者的分享. 1.前言   老于网络编程熟手来说,在测试和部署网络通信应用(比如IM聊天.实时音视频等)时,如果发现网络连接超时,第一时间想到的就是 ...

  7. Windows最常用的8个网络CMD命令总结

    一,ping 它是用来检查网络是否通畅或者网络连接速度的命令.作为一个生活在网络上的管理员或者黑客来说,ping命令是第一个必须掌握的DOS命令,它所利用的原理是这样的:网络上的机器都有唯一确定的IP ...

  8. 网络编程学习笔记-linux常用的网络命令

    网络参数设置命令 所有时刻如果你想要做好自己的网络参数设置,包括IP参数.路由参数和无线网络等,就得要了解下面这些相关的命令才行.其中Route及ip这两条命令是比较重要的.当然,比较早期的用法,我们 ...

  9. 网络编程之模拟ssh远程执行命令、粘包问题 、解决粘包问题

    目录 模拟ssh远程执行命令 服务端 客户端 粘包问题 什么是粘包 TCP发送数据的四种情况 粘包的两种情况 解决粘包问题 struct模块 解决粘包问题 服务端 客户端 模拟ssh远程执行命令 服务 ...

  10. 猫哥网络编程系列:HTTP PEM 万能调试法

    注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...

随机推荐

  1. 高效开发助手:深入了解Hutool工具库

    一.关于Hutool 1.1 简介 Hutool​是一个功能丰富且易用的Java工具库,通过诸多实用工具类的使用,旨在帮助开发者快速.便捷地完成各类开发任务. 这些封装的工具涵盖了字符串.数字.集合. ...

  2. 使用Visual Studio 调式NDK so 库时,调试工具无法显示vector内容

    最近在研究C++开发安卓端so库,demo使用xamarin.android作为载体来验证算法库文件的准确性.调试过程中发现vector中的内容无法显示集合详细.如下图 研究了半天(参考链接2.3), ...

  3. Typecho 引入 DPlayer

    想在文章中插入视频,尝试 iframe 和 video 标签后发现 m3u8 流会触发下载无法播放,用 hls 该问题后,碰到了 403 forbbiden.联想到前些天新浪图床加 referrer ...

  4. jmespath 使用及案例

    什么是jmespath jmespath 是python里面的一个库 主要在httprunner框架里使用 2.使用语法 列表: with_jmespath(jmes_path,var_name) m ...

  5. 关于我第二周学习kotlin这门语言

    有关kotlin的知识点: 在学习lambda之前,我们先了解一下什么是lambda,简答来说就是一小段代码块,并且我们可以将这个代码块在函数之间传递,这是函数式编程的一个重要特性. 通常我们会需要一 ...

  6. Web前端入门第3问:前端需要学习哪些技术?

    Web前端开发技术学习路径 基础知识 必备 HTML+CSS+JavaScript ,就目前来看,这三板斧是入门前端开发的门槛,无论如何都是逃不掉了. 进阶知识 必须会一门主流的前端框架,比如:Rea ...

  7. JMeter 定义 User 随机数变量无效

    Jmeter 定义 User 随机数变量无效 随机数方法: RandomString10 ${__RandomString(10,ABCDEFGHIJKLMNOPQRSTUVWXYZ)} Random ...

  8. docker build 镜像时,无法访问网络

    前言 在使用 docker build 命令构建 Docker 镜像时遇到无法联网的情况,可能会有多种情况的发生. 检查主机网络设置 检查你的主机是否配置了代理服务器或防火墙,这可能会阻止 Docke ...

  9. WebSocket 的产生

    HTTP 不断轮询 怎么样才能在用户不做任何操作的情况下,网页能收到消息并发生变更. 最常见的解决方案是,网页的前端代码里不断定时发 HTTP 请求到服务器,服务器收到请求后给客户端响应消息. 这种方 ...

  10. 对于 emlog pro 目前 avatar 头像不显示的问题,暂时使用这个方法解决

    avatar 头像 cdn 不稳定,目前 emlog 官方还没有放出更新包.因此,现在使用 JS 的方式暂时解决. 代码如下 <script> const avaUrl = 'https: ...