⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 18.htm

📁 UNIX环境下C编程的详细详细介绍
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<p>经被其他人所使用。(2)拨号不成功。(例如,远程系统电话占线,或者远</p>

<p>程系 </p>

<p>统关机不响应电话等)。第二种情况我们一般可以通过对调制解调器读写超时</p>

<p>来确 </p>

<p>定。(可参考练习18.10)。不管出现哪一种情况,我们都要回到拨号的第一</p>

<p>步, </p>

<p>然后选择Systems文件中同一远程系统的下一项。如同我们在图18.2中看到</p>

<p>的,一 </p>

<p>个特定的主机可以有多个项,每个主机可以有多个电话号码(同一个电话号码</p>

<p>也可 </p>

<p>以对应多个设备)。 </p>

<p>在Honey DanBer 系统中还有其他我们没有用到的文件。如Dialcodes指定</p>

<p>了S </p>

<p>ystems文件中电话号码的缩写,而Sysfiles文件允许指定Systems、</p>

<p>Devices、Dia </p>

<p>lers文件的替代文件。 </p>

<p>18.5 服务器端程序设计 </p>

<p>现在我们开始描述一下服务器端的软件。有两个因素影响服务器端软件的设计</p>

<p>。 </p>

<p>1. 拨号过程可能会延续一段时间(15-30秒),所以服务器端软件一定要创</p>

<p>建一 </p>

<p>个子进程来处理实际的拨号。 </p>

<p>2. 服务器端的精灵进程(父进程)一定要管理所有的加锁。 </p>

<p>图18.5 表示了这个过程的情况。 </p>

<p>图18.5 调制解调器拨号器的工作过程 </p>

<p>服务器的工作过程如下: </p>

<p>1. 父进程在它的服务端口,接收从客户端发来的请求。如我们在15.5节中</p>

<p>所述, </p>

<p>这在客户机-服务器之间生成了一个流管道。父进程就象15.6节中的open</p>

<p>server一 </p>

<p>样,要同时处理多个客户机。 </p>

<p>2. 基于 客户端要联系的远程系统的名字,父进程查询Systems文件和</p>

<p>Devices文 </p>

<p>件找到匹配的项。父进程同时也维护一个加锁表,记录哪些设备在被使用,这</p>

<p>样它 </p>

<p>就不查询那些被使用的项了。 </p>

<p>3. 
如果发现匹配项,创建出子进程来进行实际的拨号。(父进程这时可以</p>

<p>处理其 </p>

<p>他客户端请求)。如果成功,子进程就在客户端指定的流管道上将调制解调器</p>

<p>的文 </p>

<p>件描述符传给客户端。(这个管道在fork时也被复制了),并调用</p>

<p>exit(0)。如果 </p>

<p>发生了错误(例如,电话线占线、没有响应等),子进程调用exit(1)。 
</p>

<p>4. 在子 进程结束时,会发送信号SIGCHLD通知父进程。父进程就得到子进</p>

<p>程的结 </p>

<p>束状态(waitpid)。 </p>

<p>如果子进程成功,父进程就不用再做其它事情。在客户端结束使用调制解调器</p>

<p>之前,必须一直对调制解调器加锁。客户端指定的客户端-父进程之间的流管</p>

<p>道就 </p>

<p>一直打开着。这样,当客户端终止时,父进程得到通知,然后释放对设备的加</p>

<p>锁。 </p>

<p>如果子进程不成功,父进程就从Systems文件中尝试找下一个匹配项。如果找</p>

<p>到了对远程系统的另一项,父进程返回上一步,创建一个新的子进程来拨号。</p>

<p>如果 </p>

<p>没有找到新的匹配项,父进程调用send_err(见程序15.4)后关闭与客户端</p>

<p>的流管 </p>

<p>道。 </p>

<p>与每一个客户端有一个连接使子进程在必要时能将调试输出发回给客户端。发</p>

<p>生问题时,客户端常常想要看到整个实际拨号过程。 </p>

<p>18.6 服务器端软件源代码 </p>

<p>我们的服务器端软件共有17个文件。图18.6 
详细说明了父进程和子进程所使</p>

<p>用的 </p>

<p>文件,以及这些文件中所包含的函数。图18.7描述了不同函数的调用过程。 
</p>

<p>Source file Parent Child Functions </p>

<p>childdial.c </p>

<p>cliargs.c </p>

<p>client.c </p>

<p>ctlstr.c </p>

<p>debug.c </p>

<p>devfile.c </p>

<p>dialfile.c </p>

<p>expectstr.c </p>

<p>lock.c </p>

<p>loop.c </p>

<p>main.c </p>

<p>request.c </p>

<p>sendstr.c </p>

<p>sigchld.c </p>

<p>sysfile.c </p>

<p>ttydial.c </p>

<p>ttyopen.c </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>P </p>

<p>C </p>

<p>C </p>

<p>C </p>

<p>C </p>

<p>C </p>

<p>C </p>

<p>C </p>

<p>C child_dial </p>

<p>cli_args </p>

<p>client_allc, client_add, client_del, client_sigchld </p>

<p>ctl_str </p>

<p>DEBUG, DEBUG_NONL </p>

<p>dev_next, dev_rew, dev_find </p>

<p>dial_next, dial_rew, dev_find </p>

<p>expect_str, exp_read, sig_alrm </p>

<p>find_line, lock_set, lock_rel, is_locked </p>

<p>loop, cli_done, child_done </p>

<p>main </p>

<p>request </p>

<p>send_str </p>

<p>sig_chld </p>

<p>sys_next, sys_rew, sys_posn </p>

<p>tty_dial </p>

<p>tty_open </p>

<p>图18.6 服务器端源程序代码 </p>

<p>图18.7 服务器端的函数调用过程 </p>

<p>程序18.1是call.h 头文件,它被包含在所有这些源程序文件中。call.h</p>

<p>包含 </p>

<p>几个系统头文件,定义了一些基本的常量,声明了全局变量。 </p>

<p>_______________________________________________________________________</p>

<p>_______ </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;errno.h&gt; </p>

<p>#include &lt;signal.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>#define CS_CALL &quot;/home/stevens/calld&quot; /* well-known name */ </p>

<p>#define CL_CALL &quot;call&quot; </p>

<p>#define MAXSYSNAME 256 </p>

<p>#define MAXSPEEDSTR 256 </p>

<p>#define NALLOC 10 /* #structs to alloc/realloc for </p>

<p>*/ </p>

<p>/* Client structs (client.c), Lock structs (lock.c) */ </p>

<p>#define WHITE &quot; \t\n&quot; /* for separating tokens </p>

<p>*/ </p>

<p>#define SYSTEMS &quot;./Systems&quot; /* my own copies for now </p>

<p>*/ </p>

<p>#define DEVICES &quot;./Devices&quot; </p>

<p>#define DIALERS &quot;./Dialers&quot; </p>

<p>/* declare global variables */ </p>

<p>extern int clifd; </p>

<p>extern int debug; /* nonzero if interactive (not daemon) * </p>

<p>extern int Debug; /* nonzero for dialing debug output */ </p>

<p>extern char errmsg[]; /* error message string to return to</p>

<p>cli </p>

<p>nt */ </p>

<p>extern char *speed; /* speed (actually &quot;class&quot;) to use */ </p>

<p>extern char *sysname; /* name of system to call */ </p>

<p>extern uid_t uid; /* client's uid */ </p>

<p>extern volatile sig_atomic_t chld_flag; /* when SIGCHLD</p>

<p>occurs * </p>

<p>extern enum parity { NONE, EVEN, ODD } parity; /* specified</p>

<p>by client * </p>

<p>/ </p>

<p>typedef struct { /* one Client struct per connected client</p>

<p>*/ </p>

<p>int fd; /* fd, or -1 if available */ </p>

<p>pid_t pid; /* child pid while dialing */ </p>

<p>uid_t uid; /* client's user ID */ </p>

<p>int childdone; /* nonzero when SIGCHLD from dialing child</p>

<p>recvd: </p>

<p>1 means exit(0), 2 means exit(1) */ </p>

<p>long sysftell; /* next line to read in Systems file */ </p>

<p>long foundone; /* true if we find a matching sysfile entry</p>

<p>*/ </p>

<p>int Debug; /* option from client */ </p>

<p>enum parity parity; /* option from client */ </p>

<p>char speed[MAXSPEEDSTR]; /* option from client */ </p>

<p>char sysname[MAXSYSNAME];/* option from client */ </p>

<p>} Client; </p>

<p>extern Client *client; /* ptr to malloc'ed array of Client</p>

<p>structs */ </p>

<p>extern int client_size;/* # entries in client[] array */ </p>

<p>/* (both manipulated by client_XXX() fun </p>

<p>tions) */ </p>

<p>typedef struct { /* everything for one entry in Systems file</p>

<p>*/ </p>

<p>char *name; /* system name */ </p>

<p>char *time; /* (e.g., &quot;Any&quot;) time to call (ignored) */ </p>

<p>char *type; /* (e.g., &quot;ACU&quot;) or system name if direct</p>

<p>connect */ </p>

<p>char *class; /* (e.g., &quot;9600&quot;) speed */ </p>

<p>char *phone; /* phone number or &quot;-&quot; if direct connect */ </p>

<p>char *login; /* uucp login chat (ignored) */ </p>

<p>} Systems; </p>

<p>typedef struct { /* everything for one entry in Devices file</p>

<p>*/ </p>

<p>char *type; /* (e.g., &quot;ACU&quot;) matched by type in Systems */ </p>

<p>char *line; /* (e.g., &quot;cua0&quot;) without preceding &quot;/dev/&quot; */ </p>

<p>char *line2; /* (ignored) */ </p>

<p>char *class; /* matched by class in Systems */ </p>

<p>char *dialer; /* name of dialer in Dialers */ </p>

<p>} Devices; </p>

<p>typedef struct { /* everything for one entry in Dialers file</p>

<p>*/ </p>

<p>char *dialer; /* matched by dialer in Devices */ </p>

<p>char *sub; /* phone number substitution string (ignored) */ </p>

<p>char *expsend; /* expect/send chat */ </p>

<p>} Dialers; </p>

<p>extern Systems systems; /* filled in by sys_next() */ </p>

<p>extern Devices devices; /* filled in by dev_next() */ </p>

<p>extern Dialers dialers; /* filled in by dial_next() */ </p>

<p>/* our function prototypes */ </p>

<p>void child_dial(Client *); /* childdial.c * </p>

<p>int cli_args(int, char **); /* cliar </p>

<p>s.c */ </p>

<p>int client_add(int, uid_t); /* clien </p>

<p>..c */ </p>

<p>void client_del(int); </p>

<p>void client_sigchld(pid_t, int); </p>

<p>void loop(void); /* loop. </p>

<p>*/ </p>

<p>char *ctl_str(char); /* ctlst </p>

<p>..c */ </p>

<p>int dev_find(Devices *, const Systems *); /* devfile.c */ </p>

<p>int dev_next(Devices *); </p>

<p>void dev_rew(void); </p>

<p>int dial_find(Dialers *, const Devices *); /* dialfile.c */ </p>

<p>int dial_next(Dialers *); </p>

<p>void dial_rew(void); </p>

<p>int expect_str(int, char *); /* expec </p>

<p>str.c */ </p>

<p>int request(Client *); </p>

<p>* request.c */ </p>

<p>int send_str(int, char *, char *, int); /* sendstr.c */ </p>

<p>void sig_chld(int); /* sigch </p>

<p>d.c */ </p>

<p>long sys_next(Systems *); /* sysfile.c */ </p>

<p>void sys_posn(long); </p>

<p>void sys_rew(void); </p>

<p>int tty_open(char *, char *, enum parity, int); /* ttyopen.c</p>

<p>*/ </p>

<p>int tty_dial(int, char *, char *, char *, char *); /*</p>

<p>ttydial.c */ </p>

<p>pid_t is_locked(char *); /* lock. </p>

<p>*/ </p>

<p>void lock_set(char *, pid_t); </p>

<p>void lock_rel(pid_t); </p>

<p>void DEBUG(char *, ...); /* debug.c */ </p>

<p>void DEBUG_NONL(char *, ...); </p>

<p>_______________________________________________________________________</p>

<p>_______ </p>

<p>程序18.1 call.h 头文件 </p>

<p>我们定义了一个Client结构,它包含了每一客户的所有信息。这是一个对程</p>

<p>序 </p>

<p>15.26中类似结构的扩展。在创建一个子进程为客户端拨号和子进程终止之</p>

<p>间,我 </p>

<p>们可以处理任意多的其他客户。这个结构同时包含了我们所需要的其他信息,</p>

<p>如尝 </p>

<p>试找到Systems文件中的其他项,重新拨号等。 </p>

<p>我们同样为Systems、Devices、Dialers文件中每一项定义了一个结构。 </p>

<p>程序18.2 是这个服务器端程序的main函数。因为这个程序一般是作为精灵</p>

<p>进 </p>

<p>程运行,我们提供了一个 -d 的命令行选项,允许交互式运行。 </p>

<p>_______________________________________________________________________</p>

<p>_______ </p>

<p>#include &quot;calld.h&quot; </p>

<p>#include &lt;syslog.h&gt; </p>

<p>/* define global variables */ </p>

<p>int clifd; </p>

<p>int debug; /* daemon's command line flag */ </p>

<p>int Debug; /* Debug controlled by client, not cmd line */ </p>

<p>char errmsg[MAXLINE]; </p>

<p>char *speed; </p>

<p>char *sysname; </p>

<p>uid_t uid; </p>

<p>Client *client = NULL; </p>

<p>int client_size; </p>

<p>Systems systems; </p>

<p>Devices devices; </p>

<p>Dialers dialers; </p>

<p>volatile sig_atomic_t chld_flag; </p>

<p>enum parity parity = NONE; </p>

<p>int </p>

<p>main(int argc, char *argv[]) </p>

<p>{ </p>

<p>int c; </p>

<p>log_open(&quot;calld&quot;, LOG_PID, LOG_USER); </p>

<p>opterr = 0; /* don't want getopt() writing to stderr */ </p>

<p>while ( (c = getopt(argc, argv, &quot;d&quot;)) != EOF) { </p>

<p>switch (c) { </p>

<p>case 'd': /* debug */ </p>

<p>debug = 1; </p>

<p>break; </p>

<p>case '?': </p>

<p>log_quit(&quot;unrecognized option: -%c&quot;, optopt); </p>

<p>} </p>

<p>} </p>

<p>if (debug == 0) </p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -