网络编程-------初学者入门

一、网络相关基础知识

1、网络存在的意义
        用来做跨主机进程间的通讯,在不同的主机间进行数据交换

2、局域网
        ①一个小范围内的主机组成的网络    一般是由一个路由器构建的
        ②局域网内的主机可以互相通讯
        ③不能链接外网

3、广域网
        把无数个局域网连接在一起  组成的就是广域网
        ①通过网络运营商
        ②可以链接外网

4、连接服务器的流程
        比如:https://www.baidu.com/
        ① 输入www.baidu.com
        ② 访问DNS服务器,然后DNS服务器将域名解析成IP
        ③ 通过解析拿到的ip地址访问百度服务器

5、IP地址的分类

5.1 TCP/IP协议架构与OSI七层模型:

 TCP/IP协议:传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称作网络通讯协议。是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成

5.2 什么是IP地址以及它的作用?
IP地址(Internet Protocol Address)是指互联网协议地址,又名网络通讯协议。
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,用来区分网络中不同的计算机,网络中的IP地址是唯一的,不会出现两台电脑的IP地址一样的情况。IP地址是用点分十进制的形式表示的 A.B.C.D

        IPV4:用来给每个上网的设备  都分配一个ip地址              IPV4的地址   4个字节
                   IPV4地址的范围   0.0.0.0 - 255.255.255.255        最多支持 256*256*256*256个地址
                   运营商:临时ip,当你的设备联网的时候  会去ip池 找一个能用ip,把这个ip给你,当你的设备不联网的时候  马上回收这个ip

        IPv6:16字节 128位      能分配的地址会更多,但目前IPv6还没有铺开,仅支持,但不可用

5.3 IP地址是由两部分组成的,一个是网络id,一个是主机id,网络id指明你的计算机处于哪一个网段,主机id指明这个网段当中的具体哪一台计算机。
IP地址分为五类:
        A类:A类地址一般是超大型的企业使用
                  第一个字节是固定的(网络ID),后边三个字节是可变的(主机ID)
                  范围:  1.0.0.0-127.255.255.255
                  有127个A类地址,每个A类地址可以链接256*256*256个设备  
        B类:主要用于大型的企业
                前两个字节是固定的(网络ID),后两个字节可变(主机ID)
                范围: 128.0.0.0-191.255.255.255
                B类地址的个数:(192-128)*256
                一个B类地址可以链接256*256个设备
        C类:主要用于局域网
                 前三个字节是固定的(网络ID),后一个字节可变(主机ID)
                 范围: 192.0.0.0-223.255.255.255
                C类地址的个数:(224-192)*256*256
                 一个C类地址最多连255个设备
        D类:组播地址
                 范围: 224.0.0.0-239.255.255.255
            
        E类:保留地址
                  范围:240.0.0.0-255.255.255.255 

6、端口号
        假如系统里启动很多个进程
        比如手机   玩着王者  开着微信   QQ。借助于端口号可以来区分我要跟哪个进程进行通讯
        端口号:他就是一个16位的无符号整型数       范围:0-65535
        1-1023:被系统占用
        1024~49150:注册端口(可用)
        49151~65535:动态或私有端口
        要想通过网络去访问到某一个进程   必须要有端口号

        访问一个服务器的时候   假如使用的是http或者是https协议
            http:常见的端口     80  8080
            https:常见的端口  443  8443

7、网络字节序
        大端模式:高字节存放在低地址位,低字节存放在高地址位
        小端模式:高字节存放在高地址位,低字节存放在低字节位 
        电脑:用的是小端模式
        网络传输的时候:用的是大端模式

 

        端口号转换:(小端转大端
        uint32_t htonl(uint32_t hostlong);//本函数将一个32位数从主机字节顺序转换成无符号长整型网络字节顺序                  host    to   network  long          (将小端模式的端口转换成长整型)   
        
uint16_t htons(uint16_t hostshort);//将一个无符号短整型的主机数值转换为网络字节顺序
            host    to   network  short       (将小端模式的端口转换为无符号16位短整型

        端口号转换:(大端转小端

         uint32_t ntohl(uint32_t netlong);//将一个无符号长整形数从网络字节顺序转换为主机字节顺序。
        uint16_t ntohs(uint16_t netshort);//将一个16位数由网络字节顺序转换为主机字节顺序。

8、网络通讯   (重要)

        TCP:在通讯之前必须要建立一个网络链接,提供一个可靠的文件传输,效率略低
        UDP:在通讯之前不需要建立网络链接,提供的是不可靠的文件传输,效率略高
        TCP和UDP的区别: TCP:提供一个面向链接的可靠文件传输
                                          UDP:提供一个面向非链接的不可靠文件传输
                                          它们的通信都是使用的套接字 socket
        TCP三次握手:
                    第一次握手:建立连接时,客户端先发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)
                    第二次握手:服务器收到syn包,必须确认客户端的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
                    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
简记:①客户端先发送syn包到服务器;②服务端发送ack包和自己的syn包;③向服务器发送确认包 ACK包

UDP协议编程框架:

TCP协议编程框架:

TCP三次握手建立连接:

1、将小端模式的IP地址 转换成大端模式     inet_addr       (重要)

在网络中,IP地址是用网络字节序进行通信的,但是咱们使用的时候用的点分十进制,所以在进行网络中通信的时候,需要提前把IP地址进行转换,转换成网络字节序,也就是32位网络字节序。点分十进制形式:将地址的4个字节写成十进制数字

网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位

       函数的功能:将点分十进制的IPv4地址转换成  网络字节序列的长整型

        函数的头文件:#include <sys/socket.h>
                                 #include <netinet/in.h>
                                 #include <arpa/inet.h>

        函数的原型:in_addr_t inet_addr(const char *cp);

        函数的参数:const char *cp:要转换的IP地址

        函数的返回值:成功返回  大端模式

        例: inet_addr("192.168.10.1")

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
	in_addr_t addr;
	addr = inet_addr("192.168.18.127");//单独使用时,地址的第四个数范围为0-127
	if(addr<0)
	{
		perror("inet_addr");
		return -1;
	}
	printf("addr:%d\n",addr);
	return 0;
}
//addr:2131929280

解释:例如  inet_addr(“192.168.2.80”)的返回值,其返回值为:1342351552,这是十进制的表示方法;如果转换成2进制则为:‭01010000000000101010100011000000‬   (这个就是网络字节序),如果将上面每8位二进制中间加个".",则变成‭01010000.00000010.10101000.11000000‬,再将上面的二进制再转回成十进制,则变成了80.2.168.192,会发现与上面的192.168.2.80是相反的,即如果原来的a.b.c.d,现在经过转换就变成了d.c.b.a;

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

暂时为了解内容:

①只用于IPV4:(了解)
int inet_aton(const char *cp, struct in_addr *inp);    //点分十进制IP --> 网络字节的32位二进制数值
char *inet_ntoa(struct in_addr in);    //网络字节的32位二进制数值-->点分十进制IP

②可用于IPV4和IPV6:(了解)
int inet_pton(int af, const char *src, void *dst);//类比inet_aton    192.168.10.1 --> 11000000 10101000 00001010 00000001
const char *inet_ntop(int af, const void *src,char *dst, socklen_t cnt);  11000000 10101000 00001010 00000001 --> 192.168.10.1
af:AF_INET:表示为ipv4                AF_INET6:表示为ipv6   

2、将点分的数  转换为网络字节序的整形   inet_pton

         函数的功能:将点分十进制IP  转换为网络字节序的整型

        函数的头文件:#include <arpa/inet.h>

        函数的原型:int inet_pton(int af, const char *src, void *dst);

        函数的参数:af:要转换的ip的类型  AF_INET:ipv4     AF_INET6:ipv6
                             const char *src:点分十进制的字符串
                            void *dst:存放转换后的整形

        函数的返回值:成功返回 0       失败返回  非零

3、将网络字节序的整形数  转换成字符串   inet_ntoa

         函数的功能:点分十进制IP-->网络字节的32位二进制数值并返回

        函数的头文件:#include <sys/socket.h>
                                 #include <netinet/in.h>
                                 #include <arpa/inet.h>

        函数的原型:char *inet_ntoa(struct in_addr in);

        函数的参数:in:网络字节序的整形数  存放的位置

        函数的返回值:转换后的字符串的首地址

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

二、套接字编程

什么叫做套接字:套接字可以理解成一个特殊的文件的文件描述符,我们可以对这个描述符进行读或者写  关闭,具有很多种功能的文件
创建的时候需要借助socket函数:
                ①套接字可以绑定IP和端口
                ②套接字可以监听整个网络的信息
                ③还可以设置网络的属性
                ④可以被别人链接
                ⑤可以链接别人

1、创建一个套接字   socket        (重要)

        函数的功能:创建一个套接字

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int socket(int domain, int type, int protocol);

        函数的参数:int domain:IP的类型(AF_INET:ipv4、 AF_INET6:ipv6)
                             int type:IP协议的类型
                                            SOCK_STREAM:TCP协议     流式套接字 (提供可靠的、面向连接的通信流,保证数据传输的正确性和顺序性)        
                                            SOCK_DGRAM: UDP协议     数据报(无连接的服务,独立报文传输,无序,不保证是可靠、无差错的)
                            int protocol:固定填 0

        函数的返回值:成功返回 套接字的文件的文件描述符

文件描述符跟非缓冲区文件操作里的文件描述符 起始位置是一样的,套接字也是从 3开始的,0 1 2默认的被分配为 标准输入  标准输出  标准错误

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>

int main()
{
	int sockfd;
	sockfd=socket(AF_INET,SOCK_STREAM,0);//使用TCP协议
	printf("sockfd:%d\n",sockfd);
	return 0;
}
//sockfd:3

2、绑定服务器的属性(端口、IP)   bind

        函数的功能:将创建的监听套接字和核心结构体进行绑定

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

        函数的参数:int sockfd:套接字
                             const struct sockaddr *addr:填写的核心结构体(服务器的ip端口  以及协议类型)
                             socklen_t addrlen:核心结构体的大小

        函数的返回值:成功返回 0               失败返回  -1

struct sockaddr {                                                                                            //此结构不常用
               sa_family_t      sa_family;       ipv4或者ipv6
               char              sa_data[14];      包含ip端口,最多使用14个字符长度
           }

struct sockaddr_in {                 头文件:#include<netinet/in.h>                    //常用结构体
               sa_family_t         sin_family;      ipv4还是ipv6
               in_port_t             sin_port;         端口号
               struct in_addr     sin_addr;        存放ip地址的结构体
           };

struct in_addr {
               uint32_t       s_addr;           ip地址
           };

绑定ip 地址 绑定自己的ip即可

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>

struct sockaddr_in seraddr;
int main()
{
	int sockfd,ret;
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(8888);
	seraddr.sin_addr.s_addr=inet_addr("192.168.18.131");
	ret=bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));
	if(ret < 0)
	{
		perror("bind");
		return -1;
	}
	printf("ret:%d\n",ret);
	return 0;
}
//ret:0

3、监听整个网络   listen

        函数的功能:设置最大的监听数

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int listen(int sockfd, int backlog);

        函数的参数:int sockfd:套接字
                             int backlog:你要监听的客户端的个数,这里最大为128

        函数的返回值:成功返回 0               失败返回  -1

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
int main()
{
	int sockfd,set;
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	set=listen(sockfd,10);
	if(set < 0)
	{
		perror("listen");
		return -1;
	}
	printf("set:%d\n",set);
	return 0;
}
//set:0

4、接受客户端的链接     accept

        函数的功能:和客户端建立连接

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

        函数的参数:int sockfd:服务端的套接字
                             struct sockaddr *addr:客户端的核心结构体(用于和客户端建立连接之后保存客户端的信息,IP地址和端口号)
                             socklen_t *addrlen:存放客户端的核心结构体大小的指针

        函数的返回值:成功返回 服务器给我们虚拟出来的一个套接字,专门用来跟客户端进行通讯,假如没有客户端连接服务器  他就会阻塞                              失败返回  -1

5、接收消息   recv

        函数的功能:使用通信套接字接收发来的消息

        函数的头文件:#include <sys/types.h>
                                 #include <sys/socket.h>

        函数的原型:ssize_t recv(int s, const void *buf, size_t len, int flags);

        函数的参数:int s:通信套接字accept函数的返回值
                             const void *buf:接收到消息存放的位置
                             size_t len:接收消息的大小
                             int flags:一般写0    读取不到就阻塞

        函数的返回值:成功返回 接收到的消息的字节数                失败返回  -1         返回0,表示客户端下线

6、发送消息   send

        函数的功能:使用通信套接字发送消息

        函数的头文件:#include <sys/types.h>
                                 #include <sys/socket.h>

        函数的原型:ssize_t send(int s, const void *buf, size_t len, int flags);

        函数的参数:int s:通信套接字accept函数的返回值
                             const void *buf:要发送的消息的内容
                             size_t len:消息内容的大小
                             int flags:一般写0    发送不了就阻塞

        函数的返回值:成功返回 发送消息的字节大小                      失败返回  -1

7、连接服务器   connect

        函数的功能:向服务器发出连接请求

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

        函数的参数:int sockfd:创建的套接字,socket的返回值
                             const struct sockaddr *addr:服务端地址的结构体
                                                                 这个结构体里包含了:IP的类型、IP地址、服务器的端口
                             socklen_t addrlen:服务端的地址结构体的大小

        函数的返回值:成功返回 0                        失败返回  -1

8、关闭套接字     close

#include <stdio.h>                         //TCP服务端流程,仅能接收一次消息
#include <sys/types.h>   
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
struct sockaddr_in seraddr;
struct sockaddr cliaddr;
int main()
{
	int sockfd;
	//创建一个套接字
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("socket");
		return -1;
	}
	//绑定服务器信息,     失败的可能性比较大
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(9999);//小端转大端    //端口号,要合法
	seraddr.sin_addr.s_addr=inet_addr("192.168.18.133");//真实存在的ID,可以写自己的ID
	if(bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr))<0)
	{
		perror("bind");
		return -1;
	}
	//监听网络
	if(listen(sockfd,10)<0)
	{
		perror("listen");
		return -1;
	}
	//接收客户端的链接
	int cfd;
	socklen_t len;
	cfd=accept(sockfd,&cliaddr,&len);
	if(cfd<0)
	{
		perror("accept");
		return -1;
	}
	//此处接收客户端发来的消息
	char buf[15];
	memset(buf,0,sizeof(buf));//清空buf中的内容,初始化
	recv(cfd,buf,10,0);
	printf("buf:%s\n",buf);
	//发送一条到客户端消息
	send(cfd,"hello",5,0);
	//关闭套接字
	close(sockfd);
	return 0;
}
#include <stdio.h>            //一个客户端可以不停地发送消息,服务端可以不断地接收消息
#include <sys/types.h>   
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
struct sockaddr_in seraddr;
struct sockaddr cliaddr;

void *myfunc(void *arg)
{
	char buf[10];
	int cfd=*((int *)arg);
	printf("cfd:%d\n",cfd);
	while(1)
	{
		memset(buf,0,sizeof(buf));
		recv(cfd,buf,10,0);
		printf("buf:%s\n",buf);
		//发送一条到客户端消息
		send(cfd,"hello",5,0);//如果有一个客户端链接之后,就不发消息,那么就会发生阻塞,服务端就无法接收新的客户端,所以此时要使用多线程,专门处理这个客户端的问题
	}
}

int main()
{
	int sockfd;
	//创建一个套接字
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("socket");
		return -1;
	}
	//绑定服务器信息,     失败的可能性比较大
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(9999);//小端转大端    //端口号,要合法
	seraddr.sin_addr.s_addr=inet_addr("192.168.18.133");//真实存在的ID,可以写自己的ID
	if(bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr))<0)
	{
		perror("bind");
		return -1;
	}
	//监听网络
	if(listen(sockfd,10)<0)
	{
		perror("listen");
		return -1;
	}
	//接收客户端的链接
	int cfd;
	socklen_t len;
while(1)
{
	pthread_t pthid;
	cfd=accept(sockfd,&cliaddr,&len);
	//此处接收客户端发来的消息
	pthread_create(&pthid,NULL,myfunc,(void *)&cfd);
}
	//关闭套接字
	close(sockfd);
	return 0;
}

三、UDP通讯

非连接、不可靠、速度更快、多用于音频播放  视频播放。udp可以用来组件广播或者组播、广播和组播一般存在于局域网内

1、接收udp报文   recvfrom

        函数的功能:接收udp的消息

        函数的头文件:#include <sys/types.h>
                                 #include <sys/socket.h>

        函数的原型:ssize_t recvfrom(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

        函数的参数:int s:通信套接字accept函数的返回值
                             const void *buf:接收到的消息的存放位置
                             size_t len:接收的消息的大小
                             int flags:一般写 0       接收不到就阻塞
                             const struct sockaddr *to:客户端的核心结构体(服务端状态下,接收客户端信息)
                             socklen_t tolen:客户端的核心结构体的大小(服务端状态下)

        函数的返回值:成功返回 接收消息的字节大小                        失败返回  -1

2、发送UDP的报文   sendto

        函数的功能:发送udp消息

        函数的头文件:#include <sys/types.h>
                                 #include <sys/socket.h>

        函数的原型:ssize_t sendto(int s, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

        函数的参数:int s:通信套接字accept函数的返回值
                             const void *buf:要发送的消息的内容
                             size_t len:要发送内容的大小
                             int flags:一般写 0      不送不了就阻塞
                             const struct sockaddr *dest_addr:要发送的对象的核心结构体
                             socklen_t addrlen:要发送的对象的核心结构体的大小

        函数的返回值:成功返回 成功发送消息的字节大小                        失败返回  -1

#include<stdio.h>                                 //UDP服务端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
struct sockaddr_in seraddr,cliaddr;
int main()
{
	//1:创建一个套接字
	int sockfd,ret;
	char buf[32];
	socklen_t len;
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}
	//2:绑定服务器的ip端口
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(8888);
	seraddr.sin_addr.s_addr=inet_addr("192.168.18.133");
	ret=bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));
	if(ret < 0)
	{
		perror("bind");
		return -1;
	}
	while(1)//循环接收客户端发来的消息
	{
		//3:接收客户端发来的消息
		len=(socklen_t)sizeof(cliaddr);
		memset(buf,0,sizeof(buf));//初始化buf,防止里面存在有杂乱的东西
		recvfrom(sockfd,buf,10,0,(struct sockaddr *)&cliaddr,&len);
		//printf("len=%d\n",len);
		printf("buf=%s\n",buf);
		//printf("client:%s\n",inet_ntoa(cliaddr.sin_addr));
		//4:发送一条消息到客户端
		printf("hhhhh:%ld\n",sendto(sockfd,"hi!",5,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr)));
	}
	return 0;
}
#include<stdio.h>                          //UDP客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<unistd.h>

int main()
{
	//1:创建一个套接字
	int sockfd;
	socklen_t len;
	char buf[10];
	struct sockaddr_in seraddr,seraddr1;
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}
	//2:发送消息
	seraddr.sin_family=AF_INET;
	seraddr.sin_port=htons(8888);
	seraddr.sin_addr.s_addr=inet_addr("192.168.18.133");
	while(1)
	{
		sendto(sockfd,"world",5,0,(struct sockaddr *)&(seraddr),sizeof(seraddr));
		//3:接受消息
		memset(buf,0,sizeof(buf));
		recvfrom(sockfd,buf,10,0,(struct sockaddr *)&(seraddr1),&len);
		printf("clibuf:%s\n",buf);
		sleep(1);
	}
	return 0;
}

四、UDP的组播

组播:假如一个人创建了一个组播,另外一个进程只要加入了这个组播,创建者往组播里发东西,加入组播的所有的人都能收到创建者发的消息。类似于禁言的群聊

udp默认不打开组播和广播,要想打开组播和广播功能,需要借助一个函数:setsockopt。还需要有一个组播的地址:224.0.0.0-239.255.255.255

3、设置套接字的属性   setsockopt

        函数的功能:设置linux套接字的属性

        函数的头文件:#include <sys/types.h>          /* See NOTES */
                                 #include <sys/socket.h>

        函数的原型:int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

        函数的参数:int sockfd:套接字
                             int level:网络的层   IPPROTO_IP:用于设置组播和广播                                                                         SOL_SOCKET:用于快速释放底层ip或者广播
                            int optname:你要设置的属性
                                            属于IPPROTO_IP:IP_MULITCAST_IF:创建一个多播组                                                                                 IP_ADD_MEMBERSHIP:加入一个多播组  
                                            属于SOL_SOCKET:SO_REUSEADDR:快速释放底层ip     
                                                                               SO_BROADCAST:开启套接字的广播
                             const void *optval:参数依据前面选项的不同,参数也不同

struct ip_mreqn {
                      struct in_addr    imr_multiaddr;    要加入的多播组的ip
                      struct in_addr    imr_address;      自己的ip
                                                      
                      int            imr_ifindex;     物理网卡的索引号
                                                // if_nametoindex("ens33")
                  };

struct in_addr {
               uint32_t       s_addr;            ip 地址
           }; 

                             socklen_t optlen:前面 optval 参数的长度

        函数的返回值:成功返回 0                       失败返回  -1

#include<stdio.h>                             //创建一个多播组
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include <net/if.h>
int main()
{
	//1:创建套接字
	int sfd,ret;
	struct sockaddr_in sendaddr;
	struct ip_mreqn memmsg;
	sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		perror("socket");
		return -1;
	}
	//2:创建多播组
	memmsg.imr_multiaddr.s_addr=inet_addr("224.2.2.2");
	memmsg.imr_address.s_addr=inet_addr("192.168.18.133");
	memmsg.imr_ifindex=if_nametoindex("ens33");
	ret=setsockopt(sfd,IPPROTO_IP,IP_MULTICAST_IF,&memmsg,sizeof(memmsg));
	if(ret < 0)
	{
		perror("setsockopt");
		return -1;
	}
	//3:向组播发送消息
	while(1)
	{
		sendaddr.sin_family=AF_INET;
		sendaddr.sin_port=htons(12345);
		sendaddr.sin_addr.s_addr=inet_addr("224.2.2.2");
		
		sendto(sfd,"hello",5,0,(struct sockaddr *)&sendaddr,sizeof(sendaddr));
		sleep(2);
	}
	return 0;
}
#include<stdio.h>                           //加入一个多播组
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include <net/if.h>
int main()
{
	struct sockaddr_in cliaddr;
	struct sockaddr rcvaddr;
	struct ip_mreqn memmsg;
	char buf[10];
	socklen_t len;
	//1:创建套接字
	int cid =socket(AF_INET,SOCK_DGRAM,0);
	if(cid < 0)
	{
		perror("socket");
		return -1;
	}
	//2:绑定信息
	cliaddr.sin_family=AF_INET;
	cliaddr.sin_port=htons(12345);//要与多播组的端口保持一致
	cliaddr.sin_addr.s_addr=inet_addr("0.0.0.0");
	bind(cid,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
	//3:加入一个多播组
	memmsg.imr_multiaddr.s_addr=inet_addr("224.2.2.2");
	memmsg.imr_address.s_addr=inet_addr("192.168.18.133");
	memmsg.imr_ifindex=if_nametoindex("ens33");
	setsockopt(cid,IPPROTO_IP,IP_ADD_MEMBERSHIP,&memmsg,sizeof(memmsg));
	while(1)
	{
		recvfrom(cid,buf,10,0,&rcvaddr,&len);
		printf("接收到组播消息:%s\n",buf);
	}
	return 0;
}

五、netstat

netstat是控制台命令,是一个监控TCP/IP网络的非常有用的工具,它可以监控实际的网络连接以及每一个网络接口设备的状态信息。netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。

 查看端口占用情况:netstat -anp | grep 端口号

查看所有已使用的端口情况:netstat -nultp

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值