当前位置:首页>Linux>Linux C编程 | 从0实现telnet获取程序终端控制权

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

  • 2026-09-09 15:11:51
Linux C编程 | 从0实现telnet获取程序终端控制权

击左上方蓝色“一口Linux”,选择“设为星标”

第一时间看干货文章
☞【干货】嵌入式驱动工程师学习路线
☞【干货】Linux嵌入式知识点-思维导图-免费获取
☞【就业】一个可以写到简历的基于Linux物联网综合项目
☞【就业】简历模版

一、应用场景

在实际项目开发中,我们程序默认终端的输入输出是串口,但是调试时可能只能通过网络远程调试,而远程最常用的登录手段就是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语言实例|实现简单的命令行》 程序主要流程如下:注意:

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

四、核心代码

  • 文件
peng@ubuntu:~/work/demo/telnet/telcmd$ tree ..├── cmd.c├── color.h├── mkapp.sh├── tel└── telnet.c0 directories, 5 files
  • 主函数main()
intmain(){int server_fd, client_fd;structsockaddr_inaddr;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 connect\n"); 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()
voidswitch_to_telnet(int new_fd){ staticint old_fd = -1;// 忽略 SIGPIPE signal(SIGPIPE, SIG_IGN); cprintf(GREEN_H,"tel_dup(),%d %d\n",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 same\n"); } fflush(stdout);}
  • switch_back_to_serial()
voidswitch_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()
voidstd_bakcup(void){//保存原始标准输出(串口) original_stdout_fd = dup(STDOUT_FILENO);    original_stderr_fd = dup(STDERR_FILENO); original_stdin_fd = dup(STDIN_FILENO);}
  • create_cmd_thread()
voidcreate_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_init\n");  }   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 = streamwait = no   user = root   server = /usr/sbin/in.telnetd   log_on_failure += USERID}
  • 启动/重启服务
sudo systemctl restart xinetdsudo systemctl enable xinetd  #开机运行
  • 测试 客户端连接命令:
 telnet 127.0.0.12323

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

windows下测试

测试步骤如下:

软件配置

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

mobaXterm

xshell

六、代码获取

转发留言:telnet

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

end

一口Linux 

关注,回复【1024】海量Linux资料赠送

精彩文章合集

文章推荐

☞【专辑】ARM
☞【专辑】粉丝问答
☞【专辑】所有原创
☞【专辑】linux入门
☞【专辑】计算机网络
☞【专辑】Linux驱动
☞【干货】嵌入式驱动工程师学习路线
☞【干货】Linux嵌入式所有知识点-思维导图

最新文章

随机文章