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服务器端
TCP客户端
UDP服务器端
UDP客户端
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);
}