目录
[1. 套接字属性](#1. 套接字属性)
[1.1 套接字属性的获取和设置](#1.1 套接字属性的获取和设置)
[1.2 案例实现](#1.2 案例实现)
[2.1. 单播:](#2.1. 单播:)
[2.2. 广播:](#2.2. 广播:)
[2.3. 组播:](#2.3. 组播:)
学习内容:
1. 套接字属性
1.1 套接字属性的获取和设置
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
功能:设置套接字在不同层上的属性
参数1:套接字文件描述符
参数2:要设置的层
应用层:SOL_SOCKET
传输层:tcp传输:IPPROTO_TCP
udp传输:IPPROTO_UDP
网络层: IPPROTO_IP
参数3:要设置当前层的属性名称 ,常用每层属性见下表
参数4:要设置或者获取属性的值 ,一般为int类型
参数5:参数4的大小
返回值:成功 返回0,失败返回-1并置位错误码
1.2 案例实现
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建一个套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、获取套接字地址重用的值
int get_val = 1;
socklen_t size = sizeof(get_val); //获取属性值的大小
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &get_val, &size) == -1)
{
perror("getsockopt error");
return -1;
}
printf("get_val = %d\n", get_val); //如果结果为0,表示套接字默认不允许端口号快速重用
//3、设置允许端口号快速重用
int set_val = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &set_val, sizeof(set_val)) ==-1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
//4、验证是否设置成功了
get_val = 0;
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &get_val, &size) == -1)
{
perror("getsockopt error");
return -1;
}
printf("get_val = %d\n", get_val); //如果结果为1,表示套接字端口号快速重用设置成功
return 0;
}
二、网络通信方式
2.1. 单播:
发送端和接收端完成一对一的通信方式。目前的通信模型都是单播
2.2. 广播:
发送端和接收端完成一对多的通信方式,网络将发送端的数据,全部复制一遍发送给每个接收端一份。
2.3. 组播:
发送端和接收端完成一对多的通信方式,但是仅仅只限于加入多播组的成员。
三、广播
1> 广播是实现网络通信中一对多的通信方式,发送端用于发送数据,每个接收端都可以收到消息
2> 对于套接字而言,一般是不允许发送广播消息的,需要对发送端套接字进行设置允许广播
setsockopt ---> SOL_SOCKET ----> SO_BROADCAST ----> int
3> 广播的发送端需要绑定广播地址
广播地址:网络号 + 255
当前网络中,主机号为255的那个ip地址
4> 广播消息不允许穿过路由器,广播地址只对当前局域网中的所有主机进行消息的转发
5> 广播分为发送端和接收端,发送端用于发送数据,接收端用于接收数据
6> 广播只能使用UDP实现,接收端无论愿不愿意接收,发送端都正常发送消息
7> 广播的发送端流程 ---> UDP的客户端
1、socket(); //创建用于通信的套接字文件描述符
2、bind(); //可选,可以绑定也可以不绑定
3、setsockopt(); //设置当前套接字允许广播
4、sendto(); //向广播地址发送消息
5、close(); //关闭套接字
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
printf("sfd = %d\n", sfd);
//2、绑定可选
//3、设置允许消息广播
int broad = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("设置广播成功\n");
//4、向广播地址发送消息
//4.1 封装广播的地址信息
struct sockaddr_in bin;
bin.sin_family = AF_INET;
bin.sin_port = htons(5555);
bin.sin_addr.s_addr = inet_addr("192.168.0.255");
char wbuf[128] = "";
while(1)
{
printf("请输入>>>");
fgets(wbuf, sizeof(wbuf), stdin);
wbuf[strlen(wbuf)-1] = 0;
//发送消息
sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&bin, sizeof(bin));
printf("发送成功\n");
}
//5、关闭套接字
close(sfd);
return 0;
}
8> 广播的接收端流程 ----> UDP的服务器端
1、socket(); //创建用于通信的套接字文件描述符
2、bind(); //必须进行,但是,绑定的广播地址和端口号
3、sendto(); //向广播地址发送消息
4、close(); //关闭套接字
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if(rfd == -1)
{
perror("socket error");
return -1;
}
printf("rfd = %d\n", rfd); //3
//2、绑定广播地址和端口号
//2.1 填充地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET; //通信域
rin.sin_port = htons(5555); //端口号
rin.sin_addr.s_addr = inet_addr("192.168.0.255"); //广播地址
//2.2 绑定工作
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//3、接受数据
char rbuf[128] = "";
while(1)
{
//清空容器
bzero(rbuf, sizeof(rbuf));
//接受广播消息
recv(rfd, rbuf, sizeof(rbuf), 0);
printf("收到广播消息为:%s\n", rbuf);
}
//4、关闭套接字
close(rfd);
return 0;
}
四、组播
1> 组播也是实现一对多的通信方式,对于广播而言,网络需要对每个消息进行复制转发,会占用大量的带宽,导致网络拥塞
2> 组播可以实现小范围的数据传播:将需要接收数据的接收端加入多播组,发送端向多播组中发送消息,每个组内成员都能接收到消息
3> 需要对接收端进行设置,将接收端加入多播组
4> 组播也是使用UDP实现的,无论接收端是否愿意接收数据,发送端都可以正常向多播组中发送数据
5> 组播也分为发送端和接收端流程
6> 组播发送端流程 -----> 类似于UDP的客户端流程
1、socket(); //创建用于通信的套接字文件描述符
2、bind(); //可选,可以绑定也可以不绑定
3、sendto(); //向多播组地址发送消息
4、close(); //关闭套接字
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、socket(); //创建用于通信的套接字文件描述符
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、bind(); //可选,可以绑定也可以不绑定
//3、sendto(); //向多播组地址发送消息
struct sockaddr_in min;
min.sin_family = AF_INET;
min.sin_port = htons(9999);
min.sin_addr.s_addr = inet_addr("224.1.2.3");
char wbuf[128] = "";
while(1)
{
printf("请输入>>>>");
fgets(wbuf, sizeof(wbuf), stdin);
wbuf[strlen(wbuf)-1] = 0;
sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&min, sizeof(min));
printf("发送成功\n");
}
//4、close(); //关闭套接字
close(sfd);
return 0;
}
7> 组播的接收端流程 ------> 类似于UDP的服务器端流程
1、socket(); //创建用于通信的套接字文件描述符
2、bind(); //必须进行,但是,绑定的组播地址和端口号
3、setsockopt(); //加入多播组
4、sendto(); //向广播地址发送消息 5、close(); //关闭套接字
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、socket(); //创建用于通信的套接字文件描述符
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if(rfd == -1)
{
perror("socket error");
return -1;
}
//3、setsockopt(); //加入多播组
//准备要设置的数据
struct ip_mreqn imr;
imr.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); //组播ip
imr.imr_address.s_addr = inet_addr("192.168.0.143"); //主机ip
imr.imr_ifindex = 2; //网卡编号
if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("加入多播组成功\n");
//2、bind(); //必须进行,但是,绑定的组播地址和端口号
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(9999);
rin.sin_addr.s_addr = inet_addr("224.1.2.3"); //组播地址
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//4、recv(); //从组播地址中接受消息
char rbuf[128] = "";
while(1)
{
//清空容器
bzero(rbuf, sizeof(rbuf));
//接受广播消息
recv(rfd, rbuf, sizeof(rbuf), 0);
printf("收到组播消息为:%s\n", rbuf);
}
//5、close(); //关闭套接字
close(rfd);
return 0;
}
课外作业:
一、TCP机械臂测试
通过w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂
解析:
#include<myhead.h>
#define SER_PORT 8888 //与服务器保持一致
#define SER_IP "192.168.2.28" //服务器ip地址
#define CLI_PORT 9999 //客户端端口号
#define CLI_IP "192.168.2.227" //客户端ip地址
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd); //3
//3、连接到服务器
//3.1 填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //通信域
sin.sin_port = htons(SER_PORT); //服务器端口号
sin.sin_addr.s_addr = inet_addr(SER_IP); //服务器ip地址
//3.2 连接服务器
if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
{
perror("connect error");
return -1;
}
printf("连接服务器成功\n");
int x=30,y=30;
//准备数据
char rbuf[5] = {0xff, 0x02, 0x00, x, 0xff};
char bbuf[5] = {0xff, 0x02, 0x01, y, 0xff};
//发送给服务器,以初始化机械臂
send(cfd, rbuf,sizeof(rbuf), 0);
sleep(1);
send(cfd, bbuf,sizeof(bbuf), 0);
char a=0;
while (1)
{
printf("请输入wsad,q退出:");
scanf("%s",&a);
getchar();
if(a == 'w')
{
x += 10;
rbuf[3] = x;
send(cfd, rbuf, sizeof(rbuf), 0);
sleep(1);
}else if (a == 's')
{
x -= 10;
rbuf[3] = x;
send(cfd, rbuf, sizeof(rbuf), 0);
sleep(1);
}else if (a == 'a')
{
y -= 2;
bbuf[3] = y;
send(cfd, bbuf, sizeof(bbuf), 0);
sleep(1);
}else if (a == 'd')
{
y += 2;
bbuf[3] = y;
send(cfd, bbuf, sizeof(bbuf), 0);
sleep(1);
}else if (a == 'q')
{
break;
}
else
{
printf("输入错误请重新输入\n");
continue;
}
}
//5、关闭套接字
close(cfd);
return 0;
}