全部博文(298)
分类: LINUX
2011-04-07 13:51:27
(16)多接口设计
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1. 本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
2. 多接口设计
通常服务器所在的主机存在多个网络接口,即包含多个网络地址。某个特定服务一般采用固定的端口号,而网络地址却由所运行的主机不同而不同,因此如果服务器所在的主机存在多个网络接口,应考虑多地址绑定的问题。
服务器多地址绑定的主要两中情况:
单个服务器绑定到多个接口
多个服务器绑定到不同接口
3. 单个服务器绑定到多个接口
服务器的绑定通过bind()函数实现的,为了绑定到多个接口需要使用常量INADDR_ANY来代表本机所以有效地IP地址,当服务器绑定到所有的IP地址后,客户可以从任一地址来获得服务器服务。
例如:
服务器 2个IP地址: 192.9.200.10 和192.9.200.20
TCP服务器绑定到所有地址上并且端口号为80 如下
******************************************************************\
192.9.200.10
192.9.200.20
TCP服务器
{*.80,*.*}
(监听套接字)
******************************************************************\
{*.80,*.*}表示本地套接字所有地址上并且端口号为80,远程套接字为任何地址和任何端口。
当远程客户进行连接请求,建立连接并产生连接套接字后,连接套接字地址为客户请求地址。
******************************************************************\
192.9.200.10
192.9.200.20
TCP服务器
{*.80,*.*}
(监听套接字)
|
| fork()
|
V
TCP服务器(子进程)
{192.9.200.10.80,
201.8.220.1.1200}
|
| 连接
|
V
201.8.220.1
TCP客户机
{201.8.220.1.1200,
192.9.200.10.80}
******************************************************************\
3.1多线程服务器多接口实例
为了获得被连接的IP地址,应该调用getsockname函数,下面程序是一个多线程并发服务器(注意没消除僵死进程),他绑定于所有IP地址并且端口号为1234,当与客户建立连接之后显示被连接的IP地址以及端口号。
注意:为什么必须主机端口号要与客户机端口号一致才能连接??并且显示的端口号又不是客户机上绑定的端口号,地址也不是客户机的地址??
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 1234 /* Port that will be opened */
#define BACKLOG 2 /* Number of allowed connections */
void process_cli(int connectfd, sockaddr_in client);
main()
{
int listenfd, connectfd; /* socket descriptors */
pid_t pid;
struct sockaddr_in server; /* server's address information */
struct sockaddr_in client; /* client's address information */
socklen_t sin_size;
/* creaet TCP socket */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* handle exception */
perror("Creating socket failed.");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr = htonl (INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {
/* handle exception */
perror("Bind error.");
exit(1);
}
if(listen(listenfd,BACKLOG) == -1){ /* calls listen() */
perror("listen() error\n");
exit(1);
}
sin_size=sizeof(struct sockaddr_in);
while(1)
{
/* accept connection */
if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {
perror("accept() error\n");
exit(1);
}
/* create child process */
if ((pid=fork())>0) {
/* parent process */
close(connectfd);
continue;
}
else if (pid==0) {
/* child process */
close(listenfd);
process_cli(connectfd, client);
exit(0);
}
else {
printf("fork error\n");
exit(0);
}
}
close(listenfd); /* close listenfd */
}
void process_cli(int connectfd, sockaddr_in client)
{
sockaddr_in address;
socklen_t namelen;
getsockname(connectfd, (sockaddr *)&address, &namelen);
printf("Connected to address ( %s ) port :%d\n ",inet_ntoa(address.sin_addr),address.sin_port); //注意这里的信息 我不能理解?
close(connectfd); /* close connectfd */
}
4. 多个服务器绑定到多个接口
一个主机上经常运行多个服务器,有时他们被绑定到不同的接口上用来服务不同的客户。例如:有两个部门分别通过两个接口(192.9.200.10 和192.9.200.20 )访问服务器,这时需要启动两个服务器,一个绑定到192.9.200.10上,另一个绑定到192.9.200.20上。
4.1多个服务器绑定到多个接口实例
用户通过命令行参数输入该服务器所绑定的地址,服务器完成地址绑定后监听客户连接并为客户服务。
注意:在默认的情况下,bind()函数不能绑定两个以上的服务器到相同的端口,否则会产生“bind error: address already in use”,解决办法就是利用套接字选项SO_REUSEADDR,他允许多个服务器绑定到相同端口但是不同地址上。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 1234 /* Port that will be opened */
#define BACKLOG 2 /* Number of allowed connections */
void process_cli(int connectfd, sockaddr_in client);
main(int argc, char* argv[])
{
int listenfd, connectfd; /* socket descriptors */
pid_t pid;
struct hostent *he;
struct sockaddr_in server; /* server's address information */
struct sockaddr_in client; /* client's address information */
socklen_t sin_size;
if (argc !=2) {
printf("Usage: %s
exit(1);
}
if ((he = gethostbyname(argv[1]))==NULL){ /* calls gethostbyname() */
printf("gethostbyname() error\n");
exit(1);
}
/* Create TCP socket */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* handle exception */
perror("Creating socket failed.");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {
/* handle exception */
perror("Bind error.");
exit(1);
}
if(listen(listenfd,BACKLOG) == -1){ /* calls listen() */
perror("listen() error\n");
exit(1);
}
sin_size=sizeof(struct sockaddr_in);
while(1)
{
/* accept connection */
if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {
perror("accept() error\n");
exit(1);
}
/* create child process */
if ((pid=fork())>0) {
/* parent process */
close(connectfd);
continue;
}
else if (pid==0) {
/* child process */
close(listenfd);
process_cli(connectfd, client);
exit(0);
}
else {
printf("fork error\n");
exit(0);
}
}
close(listenfd); /* close listenfd */
}
void process_cli(int connectfd, sockaddr_in client)
{
sockaddr_in address;
socklen_t namelen;
getsockname(connectfd, (sockaddr *)&address, &namelen);
printf("Connected to address ( %s )\n ",inet_ntoa(address.sin_addr) );
close(connectfd); /* close connectfd */
}
附加内容:Linux系统在线增加多个ip