说了这么多,其实这个结构相比本文第一张图而言,只多了一个伪终端。下面具体描述各部分的实现细节。
服务端②:创建伪终端,并将master重定向至nc按照man pts中的介绍,要创建master、slave对,只需要用open系统调用打开/dev/ptmx文件,即可得到master的文件描述符。同时,在/dev/pts中已经创建了一个设备文件,表示slave端。但是,为了能让其他进程(login,shell)打开slave端,需要按照手册介绍来调用两个函数:
Before opening the pseudoterminal slave, you must pass the master's file descriptor to grantpt(3) and unlockpt(3).
具体信息可以查阅man 3 grantpt,man 3 unlockpt文档。
我们可以直接关闭(man 2 close)终端创建进程的0和1号文件描述符,把master端的文件描述符拷贝(man 2 dup)到0和1号,然后把当前进程刷成nc(man 3 exec)。这虽然是比较优雅的做法,但比较复杂。而且当没有进程打开slave的时候,nc从master处读不到数据(read返回0),会认为是EOF而结束连接。所以这里用一个笨办法:将所有从master读到的数据通过管道送给nc,将所有从nc得到的数据写入master。我们需要两个线程完成这件事。
此小节代码总结如下:
//ptmxtest.c //先是一些头文件和函数声明 #include<stdio.h> #define _XOPEN_SOURCE #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<sys/ioctl.h> /* Chown the slave to the calling user. */ externint grantpt (int __fd) __THROW; /* Release an internal lock so the slave can be opened. Call after grantpt(). */ externint unlockpt (int __fd) __THROW; /* Return the pathname of the pseudo terminal slave associated with the master FD is open on, or NULL on errors. The returned storage is good until the next call to this function. */ externchar *ptsname (int __fd) __THROW __wur; char buf[1]={'\0'}; //创建缓冲区,这里只需要大小为1字节 int main() { //创建master、slave对并解锁slave字符设备文件 int mfd = open("/dev/ptmx", O_RDWR); grantpt(mfd); unlockpt(mfd); //查询并在控制台打印slave文件位置 fprintf(stderr,"%s\n",ptsname(mfd)); int pid=fork();//分为两个进程 if(pid)//父进程从master读字节,并写入标准输出中 { while(1) { if(read(mfd,buf,1)>0) write(1,buf,1); else sleep(1); } } else//子进程从标准输入读字节,并写入master中 { while(1) { if(read(0,buf,1)>0) write(mfd,buf,1); else sleep(1); } } return 0; }
将文件保存后,打开一个终端(称为终端A),运行下列命令,在命令行中建立此程序与nc的通道: