当前位置: 首页 > 图文教程 > 服务器 > Linux服务器 > 如何编写Linux下的客户机/服务器软件

Linux服务器
linux下用cron定时执行任务的方法
.htaccess绑定域名到子目录的方法
linux apache下虚拟主机配置方法
apache 局域网访问配置方案
linux Apache服务器系统安全设置与优化
linux中mac地址绑定方法
linux托盘不断闪烁之解决方法
Apache配置 虚拟转向实例
Apache No space left on device的解决办法
Apache rewrite的重写相关的参数说明
LINUX入门级常用命令20条整理
Ubuntu设置开机自动挂载所有格式硬盘分区
5个可能被你忽略的Linux安全设置方法
学习Apache的mod rewrite、access写法
改版时保留原链接,创建新的URL的方法
rsync中文手册之使用rsync实现网站镜像和备份linux
rsync 数据同步使用详解
linux URL的301重定向代码分析
eclipse3.2.2 + MyEclipse5.5 + Tomcat5.5.27 配置数据库连接池
Apache服务器二级域名的完美实现

Linux服务器 中的 如何编写Linux下的客户机/服务器软件


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-18   浏览: 51 ::
收藏到网摘: n/a


Linux以其源代码公开闻名于世,并以其稳定性和可靠性雄霸操作系统领域,在网络应用技术方面使用得更加广泛。很久以来它就是Windows的重要对手之一。随着网络时代的来临,Linux的这种优势已变得更加突出。本文将论述如何在Linux环境下利用Socket实现客户机/服务器通信。
随着网络技术的发展,网络结构已从过去的主机/终端型、对等型发展到现在广为使用的客户机/服务器型。客户机/服务器模型应用十分广泛,在Internet上WWW,E-mail,FTP等都是基于这种模型的。在面向连接的通信模式下,服务器打开监听端口,监听网络上其它客户机向该服务器发出的连接请求,当收到一个请求信号时与该客户机建立一个连接,之后两者进行交互式的通信。具体步骤可这样组织:

服务器:
1.打开一个已知的监听端口,如smtp为25、pop3为110、ftp为21、telnet为23等。
2.在监听端口上监听客户机的连接请求,如果有客户机请求连接则建立一个连接线路。
3.在连接线路上与客户机通信。
4.通信完毕后关闭连接线路并继续监听客户机的连接请求。

客户机:
1.向指定的服务器主机及端口发出连接请求。
2.当服务器建立连接线路后与服务器进行通信。
3.通信完毕后关闭连接线路。

Linux的许多特性都非常有助于网络程序设计:首先Linux拥有POSIX.1标准库函数,socket()、bind()、listen()这几个库函数可以非常方便地实现服务器/客户机模型,有关这几个库函数的使用说明将在后边介绍。其次Linux的进程管理也非常符合服务器的工作原理,所谓进程就是程序在内存中运行时的状态,可以说进程是动态的程序。在运行着Linux操作系统的计算机中,每一个进程都有一个创建它的父进程,而且它也能创建多个子进程。在服务器端我们可以用父进程去监听客户机的连接请求,当有客户机的连接请求时父进程创建一个子进程去建立连接线路并与客户机通信,而它本身可继续监听其它客户机的连接请求,这样就可避免当有一个客户机与服务器建立连接后服务器就不能再与其它客户机通信的问题。Linux的另一个特性是它秉承了UNIX设备无关性这一优秀特征,即它通过文件描述符实现了统一的设备接口,磁盘、显示终端、音频设备、打印设备甚至网络通信都使用统一的I/O调用。这三个特性将使Linux下的网络程序设计变得易如反掌。
上述三个特性的综合利用将是这篇文章所要讲述的真谛所在。下边的客户机/服务器实现过程可以说明一二,注意与上文所述步骤的不同。

服务器:
1.打开一个已知的监听端口。
2.在监听端口上监听客户机的连接请求,当有一客户机请求连接时建立连接线路并返回通信文件描述符。
4.父进程创建一子进程,父进程关闭通信文件描述符并继续监听端口上的客户机连接请求。
3.子进程通过通信文件描述符与客户机进行通信,通信结束后终止子进程并关闭通信文件描述符。

客户机:
1.向指定的服务器主机及端口发出连接请求,请求成功将返回通信文件描述符。
2.通过通信文件描述符与服务器进行通信。
3.通信完毕后关闭通信文件描述符。


Linux的以下几个库函数是网络程序设计的核心部分,它们分别是:
(1)socket
调用方式:
#include
#include

intsocket(intdomain,inttype,intprotocol);

简要说明:
此函数为通信创建一个端口,正常调用将返回一个文件描述符,错误调用将返回-1。
domain参数有两种选择:AF_UNIX与AF_INET,其中AF_INET为Internet通信协议。
type参数也有两种选择:SOCK_STREAM用于TCP,SOCK_DGRAM用于UDP。
protocol参数通常为0。
可通过下列代码为基于TCP协议的Internet通信建立套接口传输端口:

#include
#include
#include
intsock;

if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
perror("Couldnotcreatesocket");

(2)bind
调用方式:
#include
#include

intbind(ints,conststructsockaddr*address,size_taddress_len);

简要说明:
bind英文含意是关联,捆绑。其目的就是把socket返回的套接口端口与网络上的物理位置相关联。
bind正常调用返回0,出错返回-1。此函数有三个参数:其中s为socket调用返回的文件描述符,*address设置了与网络上的物理位置相关的信息,它的类型是structsockaddr,但在Internet上它是structsockaddr_in。在socket.h中structsockaddr_in定义为:
structsockaddr_in{
shortsin_family;
u_shortsin_port;
structin_addrsin_addr;
charsin_zero[8];
};
sin_family一般为AF_INET,sin_port为端口号,由于使用不同字节顺序的机器必须作转换,故应使用宏命令htons(hosttonetworkshort)来转换端口号,sin_addr将置为INADDR_ANY。这三个值设置完成后*address参数才有意义。在编写代码时,应先设置*address参数内部各成员变量的值,再调用bind。

(3)listen
调用方式:
#include
#include

intlisten(ints,intbacklog);

简要说明:
本函数使socket端口能够接受从客户机来的连接请求,正常调用返回0,出错返回-1。
s参数为socket产生的文件描述符,backlog为所能接受客户机的最大数目。
socket,bind,listen三个函数的综合调用最终在服务器上产生一个能接受客户机请求的监听文件描述符s。

(4)accept
调用方式:
#include
#include

intaccept(ints,structsockaddr*address,int*address_len);

简要说明:
当有客户机发出连接请求时,此函数初始化这个连接。正常调用返回与客户机通信的通信文件描述符,出错返回-1。
参数s为socket调用返回的文件描述符,
address将用来存储客户机的信息,此信息由accept填入,当与客户机连接时,客户机的地址与端口将填到此处。
address_len是客户机地址长度的字节数,也由accept填入。

(5)connect
调用方式:
#include
#include

intconnect(ints,structsockaddr*address,size_taddress_len);

简要说明:
客户机调用socket建立传输端口后,调用connect来建立与远程服务器相连的连接线路。
此函数的参数调用同bind。

(6)inet_addr
调用方式:
#include
#include
#include

in_addr_tinet_addr(constchar*addstring);

简要说明:
此函数将字符串addstring表示的网络地址(如192.168.0.1)转换成32位的网络字节序二进制值,若成功返回32位二进制的网络字节序地址,若出错返回INADDR_NONE。INADDR_NONE是32位均为1的值(即255.255.255.255,它是Internet的有限广播地址),故如果要转换的addstring是255.255.255.255,函数调用将失败。

(7)fork
调用方式:
#include
#include


pid_tfork(void);

简要说明:
fork的作用是拷贝父进程的内存映象来创建子进程,两个进程将接着fork后的指令继续执行。事实上它返回两个进程控制号,对于父进程它返回子进程的进程ID,对于子进程它返回0。

可用下边的代码调用fork:

pid_tchildpid;
if((childpid=fork())=-1){
perror("Theforkfailed");
exit(1);
}
elseif(child==0){
调用子进程;
}
elseif(child>0){
调用父进程;
}


以上介绍了网络编程的有关库函数的调用方法,下面举一个客户机/服务器程序的小例子具体说明如何设计网络程序。本例介绍如何查看服务器上的时间和日期,由于daytime服务器的通用端口为13,客户机程序将通过调用13号端口对服务器上的时间和日期进行操作。


/*timeserve.c*/
/*服务器程序伪代码如下:

打开daytime监听端口;
while(客户机与服务器成功连接——成功返回通信文件描述符)
{
fork()
子进程:
{
读出当前时间;
将当前时间写入通信文件描述符;
关闭通信文件描述符;
}
父进程:
关闭通信文件描述符;
}
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

intmain(intargc,char*argv[])
{
intlistenfd,communfd;
structsockaddr_inservaddr;
pid_tchildpid;
time_ttick;
charbuf[1024];

if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("Couldnotcreatesocket");
exit(1);
}

servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=INADDR_ANY;
servaddr.sin_port=htons(13);
if(bi