UDP通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #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 : 数组的大小 - flags : 0 - src_addr : 用来保存另外一端的地址信息,不需要可以指定为NULL - addrlen : 地址的内存大小 - 返回值: 成功: 返回字节的个数 失败: 返回 -1
UDP_server.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in addr ; addr.sin_family = AF_INET; addr.sin_port = htons(5200 ); 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 recvbuf[123 ]; struct sockaddr_in cliaddr ; socklen_t len = sizeof (cliaddr); int num = recvfrom(fd, recvbuf, sizeof (recvbuf), 0 , (struct sockaddr *)&cliaddr, &len); if (num == -1 ) { perror("recvfrom" ); exit (-1 ); } char cliIP[16 ]; int cliprot; inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIP, sizeof (cliIP)); cliprot = ntohs(cliaddr.sin_port); printf ("client IP %s : , Prot : %d \n" , cliIP, cliprot); printf ("client say : %s\n " , recvbuf); sendto(fd, recvbuf, strlen (recvbuf), 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr)); } close(fd); return 0 ; }
UDP_client.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in saddr ; saddr.sin_family = AF_INET; saddr.sin_port = htons(5200 ); inet_pton(AF_INET, "127.0.0.1" , &saddr.sin_addr.s_addr); int num = 0 ; while (1 ) { char sendBuf[123 ]; struct sockaddr_in cliaddr ; socklen_t len = sizeof (cliaddr); 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 , (struct sockaddr *)&cliaddr, &len); if (num == -1 ) { perror("recvfrom" ); exit (-1 ); } printf ("server say : %s\n" , sendBuf); sleep(1 ); } close(fd); return 0 ; }
1.2广播
向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广 播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1。
a. 只能在局域网中使用。
b. 客户端需要绑定服务器广播使用的端口,才可以接收到广播消息。
1 2 3 4 5 6 7 int setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen) ; - sockfd : 文件描述符 - level : SOL_SOCKET - optname : SO_BROADCAST - optval: int 类型的值, 为1 表示允许广播, 0 表示不允许 - optlen: optval 的大小
bro_server.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } int op = 1 ; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof (op)); struct sockaddr_in cliaddr ; cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(5200 ); inet_pton(AF_INET, "192.168.148.255" , &cliaddr.sin_addr.s_addr); int num = 0 ; while (1 ) { char sendbuf[123 ]; sprintf (sendbuf, "hello, clien... ... %d\n" , num++); sendto(fd, sendbuf, strlen (sendbuf), 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr)); printf ("广播的数据: %s\n" , sendbuf); sleep(1 ); } close(fd); return 0 ; }
bro_client.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in addr ; addr.sin_family = AF_INET; addr.sin_port = htons(5200 ); 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 buf[123 ]; memset (buf, 0 , sizeof (buf)); int num = recvfrom(fd, buf, sizeof (buf), 0 , NULL , NULL ); if (num == -1 ) { perror("recvfrom" ); exit (-1 ); } printf ("senbuf sat : %s \n" , buf); } close(fd); return 0 ; }
1.3 组播(多播)
单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组 IP 接口。 单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方 案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上 的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网 使用。
a.组播既可以用于局域网,也可以用于广域网
b.客户端需要加入多播组,才能接收到多播的数据
组播地址
IP 多播通信必须依赖于 IP 多播地址,在 IPv4 中它的范围从 224.0.0.0 到 239.255.255.255 , 并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:
IP地址
说明
224.0.0.0~224.0.0.255
局部链接多播地址:是为路由协议和其它用途保留的地址,路由器并不转发属于此范围的IP包
224.0.1.0~224.0.1.255
预留多播地址:公用组播地址,可用于Internet;使用前需要申请
224.0.2.0~238.255.255.255
预留多播地址:用户可用组播地址(临时组地址),全网范围内有效
239.0.0.0~239.255.255.255
本地管理组播地址,可供组织内部使用,类似于私有 IP 地址,不能用于 Internet,可限制多播范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int setsockopt (int sockfd, int level, int optname,const void *optval, socklen_t optlen) ; - level : IPPROTO_IP - optname : IP_MULTICAST_IF - optval : struct in_addr // 客户端加入到多播组: - level : IPPROTO_IP - optname : IP_ADD_MEMBERSHIP - optval : struct ip_mreq struct ip_mreq { struct in_addr imr_multiaddr ; struct in_addr imr_interface ; }; typedef uint32_t in_addr_t ;struct in_addr { in_addr_t s_addr; };
multi_server.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } 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)); struct sockaddr_in cliaddr ; cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(5200 ); inet_pton(AF_INET, "239.0.0.10" , &cliaddr.sin_addr.s_addr); int num = 0 ; while (1 ) { char sendbuf[123 ]; sprintf (sendbuf, "hello, clien... ... %d\n" , num++); sendto(fd, sendbuf, strlen (sendbuf), 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr)); printf ("多播的数据: %s\n" , sendbuf); sleep(1 ); } close(fd); return 0 ; }
multi_client.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in ad22dr ; addr.sin_family = AF_INET; addr.sin_port = htons(5200 ); 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 buf[123 ]; memset (buf, 0 , sizeof (buf)); int num = recvfrom(fd, buf, sizeof (buf), 0 , NULL , NULL ); if (num == -1 ) { perror("recvfrom" ); exit (-1 ); } printf ("senbuf sat : %s \n" , buf); } close(fd); return 0 ; }
2. 本地套接字
本地套接字的作用:本地的进程间通信
有关系的进程间的通信
没有关系的进程间的通信
本地套接字实现流程和网络套接字类似,一般呢采用TCP的通信流程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 1. 创建监听的套接字 int lfd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0 ); 2. 监听的套接字绑定本地的套接字文件 -> server端 struct sockaddr_un addr ; 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 ; bind(lfd, addr, len); 3. 连接服务器 struct sockaddr_un serveraddr ; connect(fd, &serveraddr, sizeof (serveraddr)); 4. 通信 接收数据: read/recv 发送数据:write/send 5. 关闭连接 close();
1 2 3 4 5 #define UNIX_PATH_MAX 108 struct sockaddr_un { sa_family_t sun_family; char sun_path[UNIX_PATH_MAX]; };
ipc_server.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/un.h> #include <arpa/inet.h> #include <unistd.h> int main () { unlink("server.sock" ); 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" ); int ret = bind(lfd, (struct sockaddr*)&addr, sizeof (addr)); if (ret == -1 ) { perror("bind" ); exit (-1 ); } ret = listen(lfd, 100 ); if (ret == -1 ) { perror("listen" ); exit (-1 ); } struct sockaddr_un cliaddr ; socklen_t 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); while (1 ) { char buf[128 ]; memset (buf, 0 , sizeof (buf)); int readLen = read(cfd, buf, sizeof (buf)); if (readLen == -1 ) { perror("read" ); exit (-1 ); } else if (readLen == 0 ) { printf ("client closed ... ...\n" ); break ; } else if (readLen > 0 ) { printf ("client say %s\n" , buf); send(cfd, buf, len, 0 ); } } close(cfd); close(lfd); return 0 ; }
ipc_client.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/un.h> #include <arpa/inet.h> #include <unistd.h> int main () { unlink("client.sock" ); 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" ); int ret = bind(cfd, (struct sockaddr*)&addr, sizeof (addr)); if (ret == -1 ) { perror("bind" ); exit (-1 ); } struct sockaddr_un saddr ; saddr.sun_family = AF_LOCAL; strcpy (saddr.sun_path, "server.sock" ); ret = connect(cfd, (struct sockaddr*)&saddr, sizeof (saddr)); if (ret == -1 ) { perror("connect" ); exit (-1 ); } int num = 0 ; while (1 ) { char buf[128 ]; memset (buf, 0 , sizeof (buf)); sprintf (buf, "hello, i am client %d \n" , num++); send(cfd, buf, sizeof (buf), 0 ); printf ("client say : %s \n" , buf); int readLen = recv(cfd, buf, sizeof (buf), 0 ); if (readLen == -1 ) { perror("read" ); exit (-1 ); } else if (readLen == 0 ) { printf ("server closed ... ...\n" ); break ; } else if (readLen > 0 ) { printf ("server say %s\n" , buf); } sleep(1 ); } close(cfd); return 0 ; }
报错
之前服务器运行也会报错
服务关闭端口还没有释放就会这样
本地就相当于绑定的这个文件
bind: Address already in use
解决办法:unlink(“文件名”) // 从系统中删除一个指定文件