关于TCP和UDP区别
TCP 和 UDP 他是都是网络当中的进程进行通信的,但是他们面向的群体或者是对象是 不同的,TCP 他是一对一的,一个服务器对应一个客户端,并且他在消息发送之前会和客户 端进行,三次握手,断开的会进行四次挥手.
UDP 他是无连接的,不可靠的,会造成消 息的丢失,因为他在发送消息的时候,不会给接受者建立连接,所以他根本不关心对方有没 有没有收到消息。
单播、广播和组播
三者都是使用 UDP 协议进行数据传输的。可以这样理解, 客户端和服 务器相互通信前不需要建立连接,直接向指定的对方 ip 地址和端口号发送数据, 无论对方 是否存在,是否在线,以及是否收到数据,均不用关心。
三者区别
单播:用于两个主机之间的端对端通信;
广播:用于一个主机对整个局域网上所有主机上的数据通信,网络中的所有主机都会接 收同一份数据副本;
组播:用于对一组特定的主机进行通信,且这个特定的组内主机可以动态添加和删除多 台主机,组内所有主机收到相同一份数据副本
单播过程
服务器端
创建套接字 ->填写核心结构体( 1.网络类型 2.IP 地址 3.端口号) ->绑定 ->数据收发 ->关闭套接字
socket()->bind()->sendto()/recvfrom()->close()
客户端
创建套接字 ->填写核心结构体 (网络类型 2.IP 地址 3.端口号) ->数据收发 ->关闭套接字
socket()->sendto()/recvfrom()->close()
代码示例
//服务器
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0);
if(serfd < 0)
{
perror("socket");
return -1;
}
printf("创建套接子成功\n");
struct sockaddr_in seraddr,cliaddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
seraddr.sin_port = htons(6666);
int bin = bind(serfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
if(bin < 0)
{
perror("bind");
return -1;
}
printf("绑定成功\n");
char buf[256];
socklen_t len = sizeof(cliaddr); while(1)
{
memset(buf,0,sizeof(buf));
recvfrom(serfd,buf,sizeof(buf),0,(struct sockaddr*)&cliaddr,&len);
printf("buf:%s\n",buf);
memset(buf,0,sizeof(buf));
printf("请输入发送的消息:");
scanf("%s",buf);
sendto(serfd,buf,sizeof(buf),0,(struct sockaddr*)&cliaddr,len);
}
close(serfd);
return 0;
}
//客户端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0);
if(serfd < 0)
{
perror("socket");
return -1;
}
printf("创建套接子成功\n");
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
seraddr.sin_port = htons(6666);
char buf[256];
socklen_t len = sizeof(seraddr);
while(1)
{
memset(buf,0,sizeof(buf));
printf("请输入发送的消息:");
scanf("%s",buf);
sendto(serfd,buf,sizeof(buf),0,(struct sockaddr*)&seraddr,len);
memset(buf,0,sizeof(buf)); recvfrom(serfd,buf,sizeof(buf),0,(struct sockaddr*)&seraddr,&len);
printf("buf:%s\n",buf);
}
close(serfd);
return 0;
}
组播过程
---发送端调用流程如下:
1) 创建套接字; socket
2) 填写的核心结构体;
struct ip_mreqn ipaddr;
ipaddr.imr_address.s_addr = inet_addr(IP);
ipaddr.imr_ifindex = if_nametoindex("ens33");
ipaddr.imr_multiaddr.s_addr = inet_addr("多播组的地址");
setsockopt();
3) 填写 socket 的核心结构体;
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT);
seraddr.sin_addr.s_addr = inet_addr("多播组的地址");
4) 向多播地址发送数据; sendto();
5) 关闭套接字; close();
---接收端调用流程如下:
1) 创建套接字; socket
2) 填写核心结构体;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(发送者的端口号);
addr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取自己的 IP
3) 绑定 socket; bind();
4) 设置加入多播组核心结构体;
struct ip_mreqn ipaddr;
ipaddr.imr_address.s_addr = inet_addr(发送者的 IP);
ipaddr.imr_ifindex = if_nametoindex("ens33");
ipaddr.imr_multiaddr.s_addr = inet_addr("多播组的 IP");
5) 加入播播组; setsockopt();
6) 开始循环,接收数据; recvfrom()
7) 关闭套接字; close();
代码示例
//发送端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0);
if(serfd<0)
{
perror("socket");
return -1;
}
struct ip_mreqn ipaddr;
ipaddr.imr_address.s_addr = inet_addr("192.168.110.190");
ipaddr.imr_ifindex = if_nametoindex("ens33");
ipaddr.imr_multiaddr.s_addr = inet_addr("224.6.6.6");
setsockopt(serfd,IPPROTO_IP,IP_MULTICAST_IF,&ipaddr,sizeof(ipaddr));
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = inet_addr("224.6.6.6");
char buf[20]="正在广播";
while(1)
{
sendto(serfd,buf,sizeof(buf),0,(struct sockaddr*)&seraddr,sizeof(seraddr));
sleep(1);
}
close(serfd);
return 0;
}
//接收端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0); if(serfd<0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取自己的 IP
int bin = bind(serfd,(struct sockaddr*)&addr,sizeof(addr));
if(bin < 0)
{
perror("bind");
return -1;
}
struct ip_mreqn ipaddr;
ipaddr.imr_address.s_addr = inet_addr("192.168.110.190");
ipaddr.imr_ifindex = if_nametoindex("ens33");
ipaddr.imr_multiaddr.s_addr = inet_addr("224.6.6.6");
setsockopt(serfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&ipaddr,sizeof(ipaddr));
char buf[20];
socklen_t len = sizeof(addr);
while(1)
{
memset(buf,0,sizeof(buf));
recvfrom(serfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,&len);
printf("buf:%s\n",buf);
}
close(serfd);
return 0;
}
广播过程
---广播包发送流程如下:
1) 创建 UDP 套接字;
socket(AF_INET, SOCK_DGRAM, 0)
2) 填充广播信息结构体;struct sockaddr_in
3) 设置套接字选项允许发送广播包;
setsockopt(, ,SO_BROADCAST, ,)
4) 发送数据包;sendto( )
5)关闭套接字
---广播包接收流程如下:
1) 创建 UDP 套接字;
socket(AF_INET, SOCK_DGRAM, 0)
2) 填充广播信息结构体;struct sockaddr_in
3) 绑定地址和端口;bind( )
4) 接收数据包;recvfrom( )
5)关闭套接字
代码示例
//发送者
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0);
if(serfd < 0)
{
perror("socket");
return -1;
}
printf("创建套接子成功\n");
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr("192.168.110.255");
seraddr.sin_port = htons(6666);
int n = 1;
setsockopt(serfd,SOL_SOCKET,SO_BROADCAST,&n,sizeof(n));//设置广播
char buf[256];
socklen_t len = sizeof(seraddr); int num = 1;
int sto = 0;
while(1)
{
num++;
sendto(serfd,&num,4,0,(struct sockaddr*)&seraddr,len);
//if(sto < 0)
//{
//perror("sento error\n");
//}
sleep(1);
//printf("sento ok\n");
}
close(serfd);
return 0;
}
//接收者
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main()
{
int serfd = socket(AF_INET,SOCK_DGRAM,0);
if(serfd < 0)
{
perror("socket");
return -1;
}
printf("创建套接子成功\n");
struct sockaddr_in seraddr;
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取当前的 ip 地址 192.168.15.10
seraddr.sin_port = htons(6666);
int bin = bind(serfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
if(bin < 0)
{
perror("bind"); return -1;
}
printf("绑定成功\n");
char buf[256];
socklen_t len = sizeof(seraddr);
int num = 0;
while(1)
{
recvfrom(serfd,&num,4,0,(struct sockaddr*)&seraddr,&len);
printf("num:%d\n",num);
}
close(serfd);
return 0;
}
专用函数
函数功能:设置组播属性
函数原型:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) 函数参数:sockfd:套接字的描述符
level:网络通信的层 等级的 -- 层 -- 网络的那一层
IPPROTO_IP: -- IP 层 -- 组播(man 7 ip) 专门组播 --- 你就填这个宏
SOL_SOCKET:专门用于底层的地址释放层
0 代表 -- disable --关闭功能(默认) 1 代表 -- enable -- 打开功能
optname:选项 IPPROTO_IP:
IP_ADD_MEMBERSHIP:加入多播组
IP_MULTICAST_IF:创建一个多播组
SOL_SOCKET: SO_REUSEADDR:
当套接字关闭立刻释放底层 IP
optval:根据你传入的 optname 选项不一样 参数也不一样
optlen : optval 的字节长度
UDP 专用的发送和接收函数
函数功能:发送消息
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
函数参数:sockfd:套接字
buf:发送消息的缓冲区
len:发送消息长度
flags:写 0 代表阻塞 --- 不满足就阻塞
dest_addr:核心结构体,还是 struct sockaddr_in
addrlen:核心结构体的长度
函数返回值:成功发送数据的大小 失败-1
函数功能:接收消息
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
函数参数:sockfd:套接字
buf:接收消息的缓冲区
len:接收消息长度
flags:写 0 代表阻塞 --- 不满足就阻塞
dest_addr:核心结构体,还是 struct sockaddr_in
addrlen:核心结构体的长度 --- 需要取这个变量地址
函数返回值:成功接收数据的长度 失败-1
多播专用核心结构体
struct ip_mreqn {
struct in_addr imr_multiaddr;//多播组地址
struct in_addr imr_address; //你本身的地址
int imr_ifindex; //物理硬件 ID 号-- //
if_nametoindex("ens33");//获取硬件物理编号 // };
struct in_addr {
unsigned long s_addr; };