• 正文
  • 相关推荐
申请入驻 产业图谱

Linux C编程 | 从0实现telnet获取程序终端控制权

6小时前
110
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、应用场景

在实际项目开发中,我们程序默认终端的输入输出是串口,但是调试时可能只能通过网络远程调试,而远程最常用的登录手段就是telnet和ssh;

本文就探讨如何通过telnet远程控制我们的主程序,退出telnet客户端,再交还终端控制权。

完整代码获取见文末。

二、原理

该功能最核心的功能是利用函数dup()、dup2()。

来看下Linux手册如何描述

dup 和 dup2 是 Linux/Unix 系统级 I/O 函数,核心作用:复制文件描述符,让多个文件描述符指向同一个文件表项(共享文件偏移量、打开状态)。

它们是进程间通信、重定向标准输入输出、守护进程编程的核心函数。

dup

函数原型如下:

int dup(int oldfd);

功能

复制 oldfd返回当前进程可用的最小文件描述符

    • 新 fd 和 oldfd 指向

完全相同的文件表项

dup2

int dup2(int oldfd, int newfd);

功能

强制复制 oldfd 到 newfd

如果 newfd 已经打开,先自动关闭它(原子操作,无竞争)最终 newfd 和 oldfd 指向同一个文件

三、程序设计思路

之前写过一篇关于实现一个简单命令行的文章,本文在这个代码基础之上,实现通过telnet操作命令。 《c语言实例|实现简单的命令行》 程序主要流程如下:

注意:

    新连接的客户端会把原有客户端关闭。新的连接必须重新创建命令重启子线程。
 dup2 只是修改文件描述符指向
 但已经在等待的阻塞 I/O 不会自动迁移
 子线程还在等 旧的串口 stdin
 新 Telnet 输入根本进不去
 所以有新的连接必须重启该线程

四、核心代码

    文件
peng@ubuntu:~/work/demo/telnet/telcmd$ tree .
.
├── cmd.c
├── color.h
├── mkapp.sh
├── tel
└── telnet.c

0 directories, 5 files
    主函数main()
int main() 
{
    int server_fd, client_fd;
    struct sockaddr_in addr;
    int addr_len = sizeof(addr);
staticint telnet_cli_created = 0;

    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) 
 { 
  perror("[telnet]socket"); 
exit(1); 
 }

    //port 2323
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(TELNET_DUP_SERVER_PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, addr_len) == -1) 
 {
        perror("[telnet]bind"); 
exit(1);
    }

    listen(server_fd, 1);

 std_bakcup();
 cprintf(YEL,"[telnet]wait Telnet connectn");
 create_cmd_thread();

while(1){
     client_fd = accept(server_fd, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
     if (client_fd == -1) { 
   perror("[telnet]accept"); 
   exit(1); 
  }
  switch_to_telnet(client_fd);  
  create_cmd_thread();
 }

    close(client_fd);
    close(server_fd);
    return0;
}

    switch_to_telnet()
void switch_to_telnet(int new_fd)
{ 
staticint old_fd = -1;
// 忽略 SIGPIPE
 signal(SIGPIPE, SIG_IGN);
 cprintf(GREEN_H,"tel_dup(),%d %dn",new_fd,old_fd);

if(new_fd != old_fd){
if(old_fd>0){
   if(telnet_fd  != -1){
    close(telnet_fd );
    telnet_fd  = -1;
   }   
  }
  dup2(new_fd, STDOUT_FILENO);  // printf -> client
  dup2(new_fd, STDERR_FILENO);  // perror -> client
  dup2(new_fd, STDIN_FILENO);  // getchar <- client
  old_fd = new_fd; 
  telnet_fd  = new_fd ; 
 }else{
  cprintf(RED,"[telnet] fd is samen");
 }
 fflush(stdout);
}
    switch_back_to_serial()
void switch_back_to_serial(void)
{
 dup2(original_stdout_fd, STDOUT_FILENO);
 dup2(original_stdout_fd, STDERR_FILENO);
 dup2(original_stdin_fd, STDIN_FILENO);
 if(telnet_fd  != -1){
  close(telnet_fd );
  telnet_fd  = -1;
 }
}
    std_bakcup()
void std_bakcup(void)
{
 //保存原始标准输出(串口)
 original_stdout_fd = dup(STDOUT_FILENO);
    original_stderr_fd = dup(STDERR_FILENO);
 original_stdin_fd = dup(STDIN_FILENO);
}
    create_cmd_thread()
void create_cmd_thread(void)
{
int ret;
 pthread_attr_t attr;
staticint telnet_cli_created = 0;
static pthread_t th_cmd;

if(telnet_cli_created == 1)
 {
  pthread_cancel(th_cmd);
  telnet_cli_created = 0;
 }
if(telnet_cli_created == 0){
  ret = pthread_attr_init(&attr);
if (ret != 0)
  {
   printf("pthread_attr_initn");
  } 
  pthread_create(&th_cmd,&attr,cmdThread,NULL); 

  telnet_cli_created = 1;
 } 
}

五、测试

ubuntu下测试

ubuntu需要支持telnet服务器

    安装命令
sudo apt install xinetd telnetd -y
    修改文件**/etc/xinetd.d/telnet**
service telnet
{
   disable = no
   flags  = REUSE
   socket_type = stream
   wait = no
   user = root
   server = /usr/sbin/in.telnetd
   log_on_failure += USERID
}
    启动/重启服务
sudo systemctl restart xinetd
sudo systemctl enable xinetd  #开机运行
    测试 客户端连接命令:
 telnet 127.0.0.1 2323

可以看到telnet连接后,可以输入命令,并且返回命令结果。

windows下测试

测试步骤如下:

软件配置

Linux换行符是**n(对应asc码 0xa),windows的换行符是rn**(对应asc码 0xb 0xa),mobaXterm、xshell需要设置支持Linux换行符,否则打印格式会乱。

mobaXterm

xshell

六、代码获取

转发留言:telnet

所有c语言基础示例+应用实例 合集获取:

关注公众号【一口Linux 】,回复【1024】海量Linux资料赠送

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

公众号『一口Linux』号主彭老师,拥有15年嵌入式开发经验和培训经验。曾任职ZTE,某研究所,华清远见教学总监。拥有多篇网络协议相关专利和软件著作。精通计算机网络、Linux系统编程、ARM、Linux驱动、龙芯、物联网。原创内容基本从实际项目出发,保持原理+实践风格,适合Linux驱动新手入门和技术进阶。