一、UDP通信
服务端代码:
/* #include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); - 参数: - sockfd : 通信的fd - buf : 要发送的数据 - len : 发送数据的长度 - flags : 0 - dest_addr : 通信的另外一端的地址信息 - addrlen : 地址的内存大小 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); - 参数: - sockfd : 通信的fd - buf : 接收数据的数组 - len : 数组的大小 - src_addr:用来保存另一端的地址信息,不需要可指定为NULL - addrlen:地址内存大小 */ /*服务器端*/ #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY; //2.绑定 int ret = bind(fd, (struct sockaddr *)&addr,sizeof(addr)); //3.通信 while(1){ char recbuf[128]={0}; char ipbuf[16]; struct sockaddr_in cliaddr; int len =sizeof(cliaddr); //接收数据 int num = recvfrom(fd, recbuf,sizeof(recbuf), 0, (struct sockaddr *)&cliaddr, &len); if (num==-1){ perror("recvfrom"); exit(-1); } printf("client IP: %s, Port : %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), ntohs(cliaddr.sin_port)); //输出接收到的数据 printf("client says:%s\n", recbuf); sendto(fd, recbuf, strlen(recbuf)+1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); } close(fd); return 0; }
客户端代码:
#include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(9999); //服务器端地址 inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr); int num=0; while(1){ char sendbuf[128]={0}; sprintf(sendbuf,"hello,i am client %d\n", num++); sendto(fd, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr *)&saddr, sizeof(saddr)); //接收数据 int num = recvfrom(fd, sendbuf,sizeof(sendbuf), 0, NULL, NULL); if (num==-1){ perror("bind"); exit(-1); } //输出接收到的数据 printf("server says:%s\n", sendbuf); sleep(1); } close(fd); return 0; }
UDP协议不需要多线程多进程或者I/O复用就能实现多客户端和服务器通信,因为是不可靠的。
二、广播
向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1。
a.只能在局域网中使用。
b.客户端需要绑定服务器广播使用的端口,才可以接收到广播消息。
// 设置广播属性的函数 int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); - sockfd : 文件描述符 - level : SOL_SOCKET - optname : SO_BROADCAST - optval : int类型的值,为1表示允许广播 - optlen : optval的大小
和设置I/O端口复用的函数相同
服务端发送广播代码:
/* // 设置广播属性的函数 int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); - sockfd : 文件描述符 - level : SOL_SOCKET - optname : SO_BROADCAST - optval : int类型的值,为1表示允许广播 - optlen : optval的大小 */ /*服务器端*/ #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } //2.设置广播属性 int op=1; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof(op)); //3.创建一个广播地址 struct sockaddr_in cliaddr; cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(9999); inet_pton(AF_INET, "192.168.37.255",&cliaddr.sin_addr.s_addr); //4.通信 int num=0; while(1){ char sendbuf[128]={0}; sprintf(sendbuf,"hello client %d\n" ,num++); sendto(fd, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); printf("广播的数据: %s\n", sendbuf); sleep(1); } close(fd); return 0; }
客户端接收广播代码:
#include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } //2.客户端绑定本地IP和端口 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY; //绑定 int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret==-1){ perror("bind"); exit(-1); } while(1){ char recdbuf[128]={0}; //3.接收数据 int num = recvfrom(fd, recdbuf,sizeof(recdbuf), 0, NULL, NULL); if (num==-1){ perror("bind"); exit(-1); } //输出接收到的数据 printf("server says:%s\n", recdbuf); } close(fd); return 0; }
三、组播(多播)
单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组 IP 接口。单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。
a.组播既可以用于局域网,也可以用于广域网
b.客户端需要加入多播组,才能接收到多播的数据
IP 多播通信必须依赖于 IP 多播地址,在 IPv4 中它的范围从 224.0.0.0 到 239.255.255.255 ,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:
组播服务器端:
/* // 设置广播属性的函数 int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); - sockfd : 文件描述符 - level : SOL_SOCKET - optname : SO_BROADCAST - optval : int类型的值,为1表示允许广播 - optlen : optval的大小 */ /*服务器端*/ #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } //2.设置多播属性 struct in_addr imr_multiaddr; //初始化多播地址 inet_pton(AF_INET, "239.0.0.10", &imr_multiaddr.s_addr); setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof(imr_multiaddr)); //3.初始化客户端信息 struct sockaddr_in cliaddr; cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(9999); inet_pton(AF_INET, "239.0.0.10",&cliaddr.sin_addr.s_addr); //4.通信 int num=0; while(1){ char sendbuf[128]={0}; sprintf(sendbuf,"hello client %d\n" ,num++); sendto(fd, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); printf("组播的数据: %s\n", sendbuf); sleep(1); } close(fd); return 0; }
组播客户端:
#include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> int main(){ //1.创建用于通信的socket int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd==-1){ perror("socket"); exit(-1); } //2.客户端绑定本地IP和端口 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY; //绑定 int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret==-1){ perror("bind"); exit(-1); } //加入到多播组 struct ip_mreq op; inet_pton(AF_INET,"239.0.0.10", &op.imr_multiaddr.s_addr); op.imr_interface.s_addr = INADDR_ANY; setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&op,sizeof(op)); while(1){ char recdbuf[128]={0}; //3.接收数据 int num = recvfrom(fd, recdbuf,sizeof(recdbuf), 0, NULL, NULL); if (num==-1){ perror("bind"); exit(-1); } //输出接收到的数据 printf("server says:%s\n", recdbuf); } close(fd); return 0; }
四、本地套接字
作用:本地进程间通信
有关系没关系通信都能实现,实现流程和网络套接字类似,一般采用TCP的通信流程。
// 本地套接字通信的流程 - tcp // 服务器端 1. 创建监听的套接字 int lfd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0); 2. 监听的套接字绑定本地的套接字文件 -> server端 struct sockaddr_un addr; // 绑定成功之后,指定的sun_path中的套接字文件会自动生成。 bind(lfd, addr, len); 3. 监听 listen(lfd, 100); 4. 等待并接受连接请求 struct sockaddr_un cliaddr; int cfd = accept(lfd, &cliaddr, len); 5. 通信 接收数据:read/recv 发送数据:write/send 6. 关闭连接 close(); // 客户端的流程 1. 创建通信的套接字 int fd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0); 2. 监听的套接字绑定本地的IP 端口 struct sockaddr_un addr; // 绑定成功之后,指定的sun_path中的套接字文件会自动生成。 bind(lfd, addr, len); 3. 连接服务器 struct sockaddr_un serveraddr; connect(fd, &serveraddr, sizeof(serveraddr)); 4. 通信 接收数据:read/recv 发送数据:write/send 5. 关闭连接 close();
以下是完整代码实现:
注意在实现本地套接字通信时,要删除之前存在的同名套接字文件,保证正常进行连接,使用unlink()函数即可
服务器端:
#include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/un.h>//本地套接字头文件 int main(){ unlink("server.sock"); //1.创建套接字 int lfd = socket(AF_LOCAL, SOCK_STREAM,0); if(lfd==-1){ perror("socket"); exit(-1); } struct sockaddr_un addr; addr.sun_family = AF_LOCAL; strcpy(addr.sun_path, "server.sock"); //2.绑定本地套接字 int ret =bind(lfd,(struct sockaddr * )&addr,sizeof(addr)); if(ret==-1){ perror("bind"); exit(-1); } //3.监听 ret = listen(lfd, 100); if(ret==-1){ perror("bind"); exit(-1); } //4.等待客户端连接 struct sockaddr_un cliaddr; int len = sizeof(cliaddr); int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len); if(cfd==-1){ perror("accept"); exit(-1); } printf("client socket filename : %s\n", cliaddr.sun_path); //5.通信 while(1){ char buf[128]={0}; int len = recv(cfd, buf, sizeof(buf),0); if(len==-1){ perror("accept"); exit(-1); }else if(len==0){ printf("客户端断开连接\n"); break; }else if(len>0){ printf("client say:%s\n",buf); send(cfd,buf,sizeof(buf),0); } } close(cfd); close(lfd); return 0; }
客户端:
#include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/un.h> int main(){ unlink("client.sock"); //1.创建套接字 int cfd = socket(AF_LOCAL, SOCK_STREAM,0); if(cfd==-1){ perror("socket"); exit(-1); } struct sockaddr_un addr; addr.sun_family = AF_LOCAL; strcpy(addr.sun_path, "client.sock"); //2.绑定本地套接字 int ret =bind(cfd,(struct sockaddr * )&addr,sizeof(addr)); if(ret==-1){ perror("bind"); exit(-1); } //3.主动连接服务器 struct sockaddr_un seraddr; seraddr.sun_family = AF_LOCAL; strcpy(seraddr.sun_path,"server.sock"); ret =connect(cfd, (struct sockaddr*)&seraddr,sizeof(seraddr)); if(ret==-1){ perror("connect"); exit(-1); } //4.通信 int num=0; while(1){ char buf[128]; sprintf(buf, "hello i am client %d\n", num++); send(cfd, buf, sizeof(buf),0); printf( "client say: %s\n", buf); int len = recv(cfd, buf, sizeof(buf),0); if(len==-1){ perror("recv"); exit(-1); }else if(len==0){ printf("服务端断开连接\n"); break; }else if(len>0){ printf("server say:%s\n",buf); } sleep(1); } close(cfd); return 0; }