Linux 版 (精华区)
发信人: netiscpu (说不如做), 信区: Linux
标 题: Client/Server计算的乐趣
发信站: 哈工大紫丁香 (Mon May 17 13:29:22 1999), 转信
"Linux公报... 让Linux更富魅力!"
---------------------------------------------------------------------------
-----
Client/Server 计算的乐趣
By David Nelson 译者 arnzh <arnzh@ynmail.com>
---------------------------------------------------------------------------
-----
嗨!想要找点乐是吗?试试客户/服务器计算吧。就象是通过两个中间拉紧了绳子
的罐头合子说话一般,只不过升级到了计算机上。LINUX提供了你需要的所有工具。其
实你早就使用了客户/服务器计算了,列如:Netscape,Telnet,Ftp等等。要编写你自
己的Client/Server应用也是很容易的,可能也是很有用的。 Client/Server 计算
通过网络把两个不同的程序(客户和服务器)连在一起。如果你没有连到网上的话,练习
的时候你可以抛开网络,只让LINUX和自己说话。(但是你的LINUX安装需要配置网络)
Client/Server计算的一种通用模式是使用BSD Sockets。BSD就是伯克利软件分发
,UNIX的一个早期版本。理论上讲BSD Socket是一种IP地址和端口号的组合。IP地址定
义了计算机,端口号定义了计算机的逻辑通信信道。(在这里端口不是一种物理设备。
一个物理设备,如以太网卡,可以访问一台计算机的所有端口。)
Ivan Griffin和John Nelson在1998年的2月、3月和4月为Linux日志提供了优秀的
三方网络编程的系列文章。2月的文章包含用BSD建立一个基础Client/Server组的代码
;它包括了所有开始时特别需要的东西,你可以从SSC下载这些代码,然后从这篇文章开
始实践更多的内容。
把2333.tgz下载后,用tar -xzvf 2333.tgz命令展开它,把2332l1.txt改名为ser
ver.c,2333l2.txt改为client.c。编辑server.c把第一行开始处无关的字符"@cx:"删掉
,还要把最后一行也删掉或用/*和*/把它注释掉。同样,还须把client.c的最后一行注
释掉。用"gcc -oserver server.c"编译server.c用"gcc -oclient client.c"编译cli
ent.c.server运行在本地计算机上,所以它需要知道端口号码来定义一个socket.clie
nt运行在任意一台计算机上,所以它需要知道目标server和server的端口号,你可以使
用成千的端口号。但不要用那些已经被占用了的。你的etc/services文件列出了大部分
被用掉的端口。我发现1024端口工作得较好。
我曾说过你不必联到网络上,但是你必须配置你的计算机网络来作测试。事实上,这些
代码不能使用localhost这样的通用名来运行,你必须给出计算机的确切的名字。假定
你已经设好了你的网络,输入:
server 1024 &
来启动你的Server,然后输入:
client hostname 1024
来运行你的client,hostname是你计算机的名字或ip.如果一切正常,你应该能够类似于
下列文字的输出:
Connection request from 192.168.1.1
14: Hello, World!
第一行给出了client的ip,第二行是server给client的信息。考虑到大量代码的包围,
那句世界著名的"Hello,World"程序宣言是一个很好的入手处。确认你的Server一直在
运行,直到你用fg命令和^C(ctrl-C)来关闭它。
一个查询--响应Client/Server程序的例子
现在让我们做些更有用的事吧。同时调试两个程序是很无聊的,所以让我们先来点简单
的,模拟Client/Server对的单一程序。当你明白了其中的奥秘之后,我们再把代码分
成Client和Server两部分。在下列的程序中,client用一个叫client的函数来模拟,主
程序模拟Server: /* local test of client-server code */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char name[256] = "";
char buffer[256] = "";
void client(char *buffer)
{
printf("%s", buffer);
fgets(buffer, 256, stdin);
}
int main(int argc, char *argv[])
{
int year, age;
sprintf(buffer, "Please enter your name: ");
client(buffer);
strcpy(name, buffer);
sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);
client(buffer);
year = atoi(buffer);
age = 1998 - year;
sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);
client(buffer);
return(0);
}
你无须成为一个C语言专家来看懂以上代码,模拟的Server(main)通过数组缓冲送
出一条字符串“please enter you name”给模拟的client(client),client输出字符
串,同时从键盘读入"name",然后通过缓冲区返回。然后server问出生年,当client把
输入的字符串收集来后,server 把它换成数字并和1998相减,并把年龄作为结果返回
给client.一切都做完了,但client需要一个键盘输入来控制退出,server把"q"作为退
出的命令,更老练的写法可以把这个都给省了。这个模拟的client/server演示了在客
户和服务器间传递字符串。提出和回答问题,并进行计算。
把上面的码保存为localtest.c。用"gcc -olocaltest localtest.c来编译它,当
你运行它时可以看到下面的输出: Please enter your name: joe
Hi, joe
Please enter your year of birth: 1960
Your approximate age is 38.
Enter q to quit: q
现在让我们把它变成一个真的client/server对。把以下申明插入到server.c的开头部
分: int main(int argc, char *argv[])
{
int i, year, age;
char name[256] = "";
char buffer[256] = "";
char null_buffer[256] = "";
int serverSocket = 0,
把server.c后部的程序特征代码用如下的代码替代:
/*
* Server application specific code goes here,(这是Server应用程序特征代码)
* e.g. perform some action, respond to client etc.(执行一些工作,响应client
)*/
sprintf(buffer, "Please enter your name: ");
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
/* get name (取得姓名)*/
read(slaveSocket, buffer, sizeof(buffer));
strcpy(name, buffer);
sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
/* get year of birth (取得出生年代)*/
read(slaveSocket, buffer, sizeof(buffer));
year = atoi(buffer);
age = 1998 - year;
sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);
write(slaveSocket, buffer, strlen(buffer));
close(slaveSocket);
exit(0);
这些代码几乎和模拟的client/server中的是一样的,只不过我们用读写slaveSocket来
替代调用程序。你可以把slaveSocket看作客户和服务器通过socket来连接。 客户的代
码很简单。把下列申明插入到client.c的main函数的申明部分:
int main(int argc, char *argv[])
{
int i;
int clientSocket,
把client.c后部的程序特征代码用如下的代码替代:
/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
* Receive and respond until server stops sending messages
*/
while (0 < (status = read(clientSocket, buffer, sizeof(buffer))))
{
printf("%s", buffer);
for (i = 0; i <= 255; i++) buffer[i] = 0;
fgets(buffer, 256, stdin);
write(clientSocket, buffer, strlen(buffer));
}
close(clientSocket);
return 0;
}
同样,这些代码几乎和模拟的client/server中的是一样的。主要的区别是对clie
ntSocket的使用,另一端是server中的slaveSocket,和用来控制程序的while循环。se
rver停止传送信息时while循环就会关闭client. 像刚才一样重新编译server.c和clie
nt.c,并运行它们。这次的输出应该是这样的:
Connection request from 192.168.1.1
Please enter your name: joe
Hi, joe.
Please enter your year of birth: 1960
Your approximate age is 38.
Enter q to quit: q
现在你可以来真的了:试试用多个client来呼叫同一个server。server会用为每一个cl
ient会话启动一个进程的方法来处理并发的多个请求。这项工作由server.c的fork调用
来完成。读读fork的man帮助可以学到更多东西。
Client/Server方式的chat程序
下面是最后一个例子,一个在用户间传送信息的chat程序。这是一个早期的程序,
它只允许每个用户轮流使用,同时它还要求server保持开放一个端口。但它说明了一个
client/server对怎样运行无限制的对话,它也可以被改编成实用的程序。 把以下
申明插入到server.c的开头部分 int main(int argc, char *argv[])
{
char buffer[256] = "";
int i, serverquit = 1, clientquit = 1;
int serverSocket = 0,
把server.c后部的程序特征代码用如下的代码替代:
/*
* Server application specific code goes here,
* e.g. perform some action, respond to client etc.
*/
printf("Send q to quit.\n");
sprintf(buffer, "Hi, %s\nS: Please start chat. Send q to quit.\n", inet_nto
a(clientName.sin_addr));write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
while (serverquit != 0 && clientquit != 0)
{
status = 0;
while (status == 0)
status = read(slaveSocket, buffer, sizeof(buffer));
clientquit = strcmp(buffer, "q\n");
if (clientquit != 0)
{
printf("C: %s", buffer);
for (i = 0; i <= 255; i++) buffer[i] = 0;
printf("S: ");
fgets(buffer, 256, stdin);
serverquit = strcmp(buffer, "q\n");
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
}
}
printf("Goodbye\n");
close(slaveSocket);
exit(0);
把下列申明插入到client.c的main函数的申明部分。
int main(int argc, char *argv[])
{
int i, serverquit = 1, clientquit = 1;
int clientSocket,
把client.c后部的程序特征代码用如下的代码替代:
/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
*/
while (serverquit != 0 && clientquit != 0)
{
status = 0;
while (status == 0)
status = read(clientSocket, buffer, sizeof(buffer));
serverquit = strcmp(buffer, "q\n");
if (serverquit != 0)
{
printf("S: %s", buffer);
for (i = 0; i <= 255; i++) buffer[i] = 0;
printf("C: ");
fgets(buffer, 256, stdin);
clientquit = strcmp(buffer, "q\n");
write(clientSocket, buffer, strlen(buffer));
for (i = 0; i <= 255; i++) buffer[i] = 0;
}
}
printf("Goodbye\n");
close(clientSocket);
return 0;
}
重编译server.c和client.c。为了模拟两台计算机,打开两个X的窗口或使用两个不同
的终端。(alt-1和alt-2)用 server 1024
在一个窗口中运行server,用
client hostname 1024
在另一个窗口中运行client,hostname用你实际的hostname或IP address来替代。ser
ver和client的代码和前面的例子都非常相似。只有两点主要的区别。首先是检测双方
谁输入了"q"来退出程序。serverquit和clientquit用来作为标志。第二是等待另一方
的回应的循环。read函数返回从socket读来的字符的数量;这些存储到status中。一个
非零的字符数表示另一方有消息送到。
下面是server打印的一个例子:
Connection request from 192.168.1.1
Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
Goodbye
下面是client打印的一个例子:
S: Hi, 192.168.1.1
S: Please start chat. Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
C: q
Goodbye
我希望这些例子显示了建立一个client/server计算有多简单。也许你试试自己的应用
的胃口被掉起来了。如果你作出了什么可口的东西,不要忘了让我们都知道,还有就是
一定要保持语句简洁。
---------------------------------------------------------------------------
-----
版权所有 (C) 1998, John Kacur
出版于第33期《Linux公报》1998年10月 中文版第一期
---------------------------------------------------------------------------
-----
---------------------------------------------------------------------------
-----
---------------------------------------------------------------------------
-----
--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: bin@mtlab.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:200.297毫秒