利用socket 编程接口实现本地进程间通信
UNIX域协议套接字:可以使用TCP,也可以使用UDPSOCK_STREAM -----> TCP 面向字节流
SOCK_DGRAM -----> UDP 面向数据报
UNIX域协议并不是一个实际的协议族,而是在单个主机上执行客户 / 服务器 通信的一种方式 (IPC的一种方法)UNIX域数据报 (SOCK_DGRAM)是可靠的,不会丢失消息
IP协议中使用 IP地址 + 端口号标识客户端和服务端UNIX域协议使用普通文件系统中的路径名 (绝对路径) 标识客户端和服务端
如果这个路径不存在则创建后标识其为 客户/服务器 的地址 如果这个路径已经存在了则会报错:bind failed: Address already in use 所以在运行前先删掉,再去创建并且绑定 unlink(绝对路径名);
UNIX域协议的特点:
和TCP比较,速度快,数据报不要传递到主机外,也不需要进行封包和拆包
UNIX域协议使用一个绝对路径作为“IP”地址
#include
//vim /usr/include/linux/un.h #define UNIX_PATH_MAX 108 // Unix域协议的地址结构体的具体描述 // #include
struct sockaddr_un { __kernel_sa_family_t sun_family; // 协议族 char sun_path[UNIX_PATH_MAX]; /* Unix域协议地址,是以'\0'结束的本地文件系统中的绝对路径名, 如: "/tmp/xxx.socket" */ } 编程方法:TCP / UDP
问题:客户端成功发送数据出去,服务端成功接收到数据,但是并没有成功将客户端的地址打印出来?客户端没有绑定地址。如果说服务端要向客户端发送数据的话,没有“IP”地址就没办法定位对方的具体位置了,所以要实现能获取客户端地址,则客户端需要绑定地址。
使用UDP实现UNIX域协议如果客户端不绑定,服务器就不能发送信息给客户端
2.1 基于TCP实现UNIX域协议
unix_tcp_client.c
#include
#include #include #include #include #include #include #define SERVICEPATHNAME "/home/china/s" #define CLIENTPATHNAME "/home/china/c" int main(int argc, char *argv[]) { unlink(CLIENTPATHNAME); // (1) socket:创建一个套接字 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd == -1) { perror("unix socket failed"); return -1; } // (2) bind // 需要一个网络地址结构体 struct sockaddr_un cAddr; memset(&cAddr, 0, sizeof(cAddr)); cAddr.sun_family = AF_UNIX; // 协议族 strcpy(cAddr.sun_path, SLIENTPATHNAME); int res = bind(sockfd, (struct sockaddr *)&cAddr, sizeof(cAddr)); if (res == -1) { perror("unix bind failed"); close(sockfd); return -1; } printf("bind success\n"); // (3) connect struct sockaddr_un sAddr; memset(&sAddr, 0, sizeof(sAddr)); sAddr.sun_family = AF_UNIX; // 协议族 strcpy(sAddr.sun_path, SERVICEPATHNMAE); res = connect(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr)); if (res == -1) { perror("unix connect failed"); return -1; } printf("unix connect succeed\n"); while (1) { // (3) 进行通信,读写数据 char buf[250] = {0}; scanf("%s", buf); int w = send(sockfd, buf, strlen(buf), 0); printf("w = %d\n", w); if (buf[0] == '#') { break; } char buff[250] = {0}; int r = recv(sockfd, buff, 250, 0); printf("r = %d,message = %s\n", r, buff); } // (4) 关闭socket套接字 close(sockfd); return 0; } unix_tcp_server.c
#include
#include #include #include #include #include #include #define SERVICEPATHNAME "/home/china/s" int main(int argc, char *argv[]) { unlink(SERVICEPATHNMAE); // (1) socket:创建一个套接字 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd == -1) { perror("unix socket failed"); return -1; } // (2) bind:把一个套接字和网络地址绑定到一起 // 需要一个网络地址结构体 struct sockaddr_un sAddr; memset(&sAddr, 0, sizeof(sAddr)); sAddr.sun_family = AF_UNIX; // 协议族 strcpy(sAddr.sun_path, SERVICEPATHNAME); int res = bind(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr)); if (res == -1) { perror("unix bind failed"); close(sockfd); return -1; } printf("bind success\n"); // (3) listen:让套接字进入“监听模式” res = listen(sockfd, 5); if (res == -1) { perror("unix listen failed"); close(sockfd); return -1; } // 保存客户端的网络地址 struct sockaddr_un cAddr; // 保存客户端的网络地址的长度 socklen_t addrlen = sizeof(cAddr); // (4) 当没有客户端请求的时候,accept是阻塞的 int confd = accept(sockfd, (struct sockaddr *)&cAddr, &addrlen); if (confd == -1) { close(sockfd); return -1; } printf("accept success\n"); printf("client path:%s\n", cAddr.sun_path); while (1) { // (5) 进行通信,读写数据 char buf[250] = {0}; int r = recv(confd, buf, 250, 0); if (r != -1) { printf("r = %d,messagee = %s\n", r, buf); } if (buf[0] == '#') { close(confd); break; } send(confd, "hello", sizeof("hello"), 0); } // (6) 关闭socket套接字:"四次挥手" close(sockfd); return 0; } 2.2 基于UDP实现UNIX域协议
unix_udp_client.c
#include
#include #include #include #include #include #include #define SERVICEPATHNAME "/home/china/s" #define CLIENTPATHNAME "/home/china/c" int main(int argc, char *argv[]) { unlink(CLIENTPATHNAME); // 1. socket int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sockfd == -1) { perror("udp socket failed"); return -1; } // 2. 必须bind // 需要一个网络地址结构体 struct sockaddr_un cAddr; memset(&cAddr, 0, sizeof(cAddr)); cAddr.sun_family = AF_UNIX; // 协议族 strcpy(cAddr.sun_path, CLIENTPATHNAME); int res = bind(sockfd, (struct sockaddr *)&cAddr, sizeof(cAddr)); if (res == -1) { perror("unix bind failed"); close(sockfd); return -1; } // 需要一个网络地址,指定发给哪一个服务器 struct sockaddr_un sAddr; memset(&sAddr, 0, sizeof(sAddr)); sAddr.sun_family = AF_UNIX; // 协议族 strcpy(sAddr.sun_path, SERVICEPATHNAME); // 当服务器发信息给我时,保存该服务器的网络地址 struct sockaddr_un server_addr; socklen_t addrlen = sizeof(server_addr); while (1) { // 2. sendto char buf[250] = {0}; scanf("%s", buf); int w = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&sAddr, sizeof(sAddr)); printf("w = %d\n", w); if (buf[0] == '#') { // 退出条件 break; } // 3. recvfrom char buff[250] = {0}; int r = recvfrom(sockfd, buff, 250, 0, (struct sockaddr*)&server_addr, &addrlen); if (r > 0) { printf("r = %d, buff = %s\n", r, buff); printf("server path:%s\n", server_addr.sun_path); } } // 4. close close(sockfd); return 0; } unix_udp_server.c
#include
#include #include #include #include #include #include #define SERVICEPATHNMAE "/home/china/s" int main(int argc, char *argv[]) { unlink(SERVICEPATHNMAE); // 1. socket int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sockfd == -1) { perror("unix socket failed"); return -1; } // 2. bind // 需要一个网络地址结构体 struct sockaddr_un sAddr; memset(&sAddr, 0, sizeof(sAddr)); sAddr.sun_family = AF_UNIX; // 协议族 strcpy(sAddr.sun_path, SERVICEPATHNMAE); int res = bind(sockfd, (struct sockaddr *)&sAddr, sizeof(sAddr)); if (res == -1) { perror("unix bind failed"); close(sockfd); return -1; } // 保存客户端的网络地址 struct sockaddr_un cAddr; // 保存客户端的网络地址的长度 socklen_t addrlen = sizeof(cAddr); while (1) { // 3. recvfrom char buf[250] = {0}; int r = recvfrom(sockfd, buf, 250, 0, (struct sockaddr *)&cAddr, &addrlen); if (r > 0) { printf("r = %d, buf = %s\n", r, buf); printf("client path:%s\n", cAddr.sun_path); } // 4. sendto char buff[250] = {0}; scanf("%s", buff); int w = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&cAddr, addrlen); printf("w = %d\n", w); } // 5. close close(sockfd); return 0; }