域服务器广播消息,广播,组播和UNIX域套接字

  • Post author:
  • Post category:其他


1.广播

1.特点

一对多

仅能使用UDP

2.概念

发送方只有一个接收方则称单播

如果同时发给局域网中的所有主机,成为广播

只有用户数据包(使用UDP协议)套接字才能广播

广播地址

1.以192.168.1.0(255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

2.发送到该地址的数据包将被所有主机接收

3.255.255.255.255在所有网段中都代表广播地址

3.广播发送步骤

创建用户数据报套接字

缺省创建的套接字不允许广播数据包,需要设置属性setsockopt

接收方地址指定为广播地址

指定端口信息

发送数据包

4.广播接收步骤

创建用户数据报套接字

绑定本机IP地址和端口(绑定的端口必须与发送方指定的端口相同)

等待接收数据

5.代码

与UDP大同小异

发送方需要将套接字设定为允许广播

int is_use_brc = 1;

setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &is_use_brc, sizeof(is_use_brc));

发送方绑定的IP地址为广播地址

./sender 192.168.1.255 5002

2.组播

1.概念

单播方式只能有一个接收方

广播方式发送给所有主机.过多的广播会大量占用网络带宽,造成网络风暴,影响正常通信

组播(又称多播)为单播与广播的一种折中方式,只有加入到某个组的主机才能收到数据

多播方式既能发送给多个主机,又能避免像广播那样带来过多负载(即每台主机要到传输层才能判断广播包是否需要处理)

2.组播IP地址段

224.0.0.1~239.255.255.254(中间除去广播IP)

3.组播发送步骤

创建用户数据报套接字

接收方地址设定为组播地址

指定端口信息

发送数据报

4.组播接收步骤

创建用户数据报套接字

加入多播组

绑定本机IP地址与端口(绑定的端口必须与发送方指定的端口相同)

等待数据接收

5.代码

接收方需要加入多播组

/*加入多播组*/

#define MULTICAST_IP “235.10.10.3”

struct ip_mreq mreq;

bzero(&mreq, sizeof(mreq));

mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);/*多播组的IP地址*/

mreq.imr_interface.s_addr = htonl(INADDR_ANY);/*加入的客服端主机IP地址*/

setsockopt(fd, SOL_SOCKET, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

发送方绑定地址为多播地址

./sender 235.10.10.3 5002

3.UNIX域套接字

1.特点

socket同样可以用于本地通信

创建套接字的时候需使用本地协议AF_UNIX/AF_LOCAL

socket(AF_LOCAL, SOCK_STREAM, 0);

socket(AF_LOCAL, SOCK_DGRAM, 0);

分为流式套接字与用户数据报套接字

与其他进程之间通信方式相比效率更高,使用更方便

常用于前后台进程通信

2.总结

进程之间通信方式有6种:管道,消息队列,共享内存,UNIX域套接字,信号,信号量

按照使用频率排序:消息队列>UNIX域套接字>管道>共享内存(经常需要与信号量一起使用)

按照效率排序:共享内存>UNIX域套接字>管道>消息队列

1.进程之间数据共享:管道,消息队列,共享内存,UNIX域套接字

2.异步通信:信号

3.同步与互斥,做资源保护:信号量

3.代码

大体与网络编程步骤一样,只是在绑定的时候其结构体不同,网络编程为sockaddr_in,域套接字为sockaddr_un

#include

#define UNIX_PATH_MAX 108

struct sockaddr_un

{

sa_family_t sun_family;/*AF_UNIX/AF_LOCAL*/

char sun_path[UNIX_PATH_MAX ];/*域套接字文件路径名*/

};

注意:域套接字文件路径名必须事先不存在且一般使用绝对路径,该文件存在于内存中

4.图示

TCP服务器端

31a9d2d977df

TCP客户端

31a9d2d977df

UDP服务器端

31a9d2d977df

UDP客户端

31a9d2d977df

5.示例

客户端

#include “common.h”

void tips(char *s)

{

printf(“\n%s unix_domain_file\n\n”, s);

}

int main(int argc, char **argv)

{

int fd = -1;

struct sockaddr_un sin;

char buff[BUFSIZ];

int ret = -1/*write函数返回值 */;

fd_set rset;/*定义读集合 */

int maxfd = -1;/*保存最大的文件描述符 */

struct timeval tout;/*超时时间结构体 */

/*创建socket */

if(argc != 2)

{

tips(argv[0]);

exit(-1);

}

if((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)

{

perror(“socket”);

exit(-1);

}

/*判断UNIX_DOMAIN_FILE文件是否存在且可写*/

if(access(UNIX_DOMAIN_FILE, F_OK|W_OK) < 0)

{

puts(“UNIX_DOMAIN_FILE is unkonwn”);

exit(-1);

}

/*结构体成员清零 */

bzero(&sin, sizeof(sin));

/*填充结构体 */

sin.sun_family = AF_LOCAL;

strncpy(sin.sun_path, UNIX_DOMAIN_FILE, strlen(UNIX_DOMAIN_FILE));

/*连接服务器 */

if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)

{

perror(“connect”);

exit(-1);

}

puts(“Unix domain client init ok…”);

while (1)

{

/*读集合清零 */

FD_ZERO(&rset);

/*加入进程默认打开的标准键盘输入fd */

FD_SET(0, &rset);

/*加入客户端与服务器通信的fd */

FD_SET(fd, &rset);

maxfd = fd;

tout.tv_sec = 5;/*超时时间为5秒 */

tout.tv_usec = 0;

select(maxfd + 1, &rset, NULL, NULL, &tout);

/*代表标准键盘有输入 */

if(FD_ISSET(0, &rset))

{

bzero(buff, BUFSIZ);

/*-1防止数组下标越界 */

do

{

ret = read(0, buff, BUFSIZ – 1);

} while (ret < 0 && EINTR == errno);

if(ret < 0)

{

perror(“read from keyboard”);

continue;

}

/*如果ret=0表示没有从键盘上读到数据 */

if(!ret)

{

continue;

}

if(write(fd, buff, strlen(buff)) < 0)

{

perror(“write to socket”);

continue;

}

if(strcmp(buff, QUIT_STR) == 0)

{

printf(“Client is exting…\n”);

break;

}

}

/*代表有服务器的数据到达 */

if(FD_ISSET(fd, &rset))

{

bzero(buff, BUFSIZ);

/*-1防止数组下标越界 */

do

{

ret = read(fd, buff, BUFSIZ – 1);

} while (ret < 0 && EINTR == errno);

if(ret < 0)

{

perror(“read from server”);

continue;

}

/*如果ret=0表示服务器关闭 */

if(!ret)

{

break;

}

printf(“recv from server:%s\n”, buff);

/*此处存在BUG,待修复 */

if(strlen(buff) > strlen(SERV_RESP_STR))

{

if(strcmp(buff + strlen(SERV_RESP_STR), QUIT_STR) == 0)

{

printf(“sender is exting…\n”);

break;

}

}

}

}

close(fd);

return 0;

}

服务器

#include “common.h”

void client_fork(void *arg);

/*子进程结束信号处理函数 */

void sig_child_handle(int signo)

{

/*回收子进程 */

if(SIGCHLD == signo)

{

waitpid(-1, NULL, WNOHANG);

}

}

int main(void)

{

pid_t pid;

int fd = -1;

int newfd = -1;

int b_reuse = 1;

struct sockaddr_un sun;/*定义本地通信套接字*/

signal(SIGCHLD, sig_child_handle);

/*创建本地域套接字 */

if((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)

{

perror(“socket”);

exit(-1);

}

/*允许绑定地址快速重用 */

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

/*结构体成员清零 */

bzero(&sun, sizeof(sun));

/*填充结构体 */

sun.sun_family = AF_LOCAL;

/*如果UNIX指定文件存在则删除该文件*/

if(!access(UNIX_DOMAIN_FILE, F_OK))

{

unlink(UNIX_DOMAIN_FILE);

}

strncpy(sun.sun_path, UNIX_DOMAIN_FILE, strlen(UNIX_DOMAIN_FILE));

/*绑定socket */

if(bind(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)

{

perror(“bind”);

exit(-1);

}

/*主动套接字转被动套接字 */

if(listen(fd, BACKLOG) < 0)

{

perror(“listen”);

exit(-1);

}

puts(“Server staring ok”);

while(1)

{

/*阻塞等待客户端连接请求并保存客户端信息 */

if((newfd = accept(fd, NULL, NULL)) < 0)

{

perror(“accept”);

break;

}

/*创建子进程,用于处理客户端数据 */

if((pid = fork()) < 0)

{

perror(“fork”);

break;

}

/*子进程 */

if(0 == pid)

{

close(fd);

/*客户端IP地址网络字节序转本地字节序 */

client_fork(&newfd);

return 0;

}

/*父进程 */

else

{

close(newfd);

}

}

close(fd);

return 0;

}

void client_fork(void * arg)

{

char buf[BUFSIZ];

char resp_buf[BUFSIZ + 10];

int ret = -1;/*read函数返回值 */

int newfd = *(int *)arg;

printf(“process newfd = %d\n”, newfd);

while(1)

{

/*与newfd进行读写 */

bzero(buf, BUFSIZ);

/*判断读函数是否出错 */

do

{

ret = read(newfd, buf, BUFSIZ – 1);

} while (ret < 0 && EINTR == errno);

if(ret < 0)

{

perror(“read”);

exit(-1);

}

if(0 == ret)

{

break;

}

printf(“Unix domain service receive data: %s\n”, buf);

bzero(resp_buf, BUFSIZ + 10);

strncpy(resp_buf, SERV_RESP_STR, strlen(SERV_RESP_STR));

strcat(resp_buf, buf);

do

{

ret = write(newfd, resp_buf, strlen(resp_buf));

} while (ret < 0 && EINTR == errno);

if(strcmp(buf, QUIT_STR) == 0)

{

printf(“Client is exting…\n”);

break;

}

}

close(newfd);

}