手机网站设计方案,网站快备案,上海网站设计见建设,免费软件app下载大全正能量网站Unix套接字是一种用于在同一台计算机上的进程间通信的一种机制。它是Linux和其他类Unix系统中的一项特性#xff0c;通过在文件系统中创建特殊的套接字文件#xff0c;进程可以通过这些套接字文件进行通信。 文章目录 1 Unix和TCP套接字对比2 Unix套接字初始化流程3 例:服务端…Unix套接字是一种用于在同一台计算机上的进程间通信的一种机制。它是Linux和其他类Unix系统中的一项特性通过在文件系统中创建特殊的套接字文件进程可以通过这些套接字文件进行通信。 文章目录 1 Unix和TCP套接字对比2 Unix套接字初始化流程3 例:服务端/客户端通信实现3.1 服务端3.2 客户端3.3 实验结果3.4 完整代码 4 TCP与Unix中connect的区别 1 Unix和TCP套接字对比
Unix套接字适用于在同一计算机上运行的进程之间的通信而TCP/IP套接字则设计用于在不同计算机上运行的程序之间通过网络进行通信。
比较因素Unix套接字TCP/IP套接字全名也被称为进程间通信(IPC)套接字传输控制协议/互联网协议(TCP/IP)套接字功能用于同一台计算机上运行的进程之间通信用于在不同计算机上运行的程序之间通信要求进程之间通信无需主机名和端口号使用TCP/IP套接字进行程序通信需要主机名和端口号速度由于进程在同一系统上运行避免了一些检查和操作因此通信速度较快相对于Unix套接字在网络上进行通信时速度较慢
2 Unix套接字初始化流程
在Linux中进行Unix套接字编程通常涉及一系列系统调用和相关函数。下面是一个简要的介绍以及在Unix环境中如何使用相关函数的一些例子
1、创建套接字
int socket(int domain, int type, int protocol);在Unix域套接字编程中domain参数通常设置为AF_UNIX。例
int sockfd socket(AF_UNIX, SOCK_STREAM, 0);type参数表示套接字的类型可以选择SOCK_STREAM或SOCK_DGRAM取决于通信需求。
2、绑定套接字
使用bind()系统调用将套接字绑定到一个特定的文件路径。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);在Unix域套接字编程中addr是一个指向struct sockaddr_un类型的指针用于指定套接字的路径。例
struct sockaddr_un addr;
addr.sun_family AF_UNIX;
strcpy(addr.sun_path, /tmp/my_socket);bind(sockfd, (struct sockaddr*)addr, sizeof(addr));3、监听连接
使用listen()指定套接字处于被动监听状态。
int listen(int sockfd, int backlog);backlog参数指定等待连接的队列长度。例
listen(sockfd, 5);4、接受连接
使用accept()系统调用接受来自客户端的连接请求。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);在Unix域套接字编程中addr是一个指向struct sockaddr_un类型的指针用于存储客户端的地址信息。
5、发送和接收数据
使用send()和recv()系统调用在套接字之间传输数据。
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);6、关闭套接字
使用close()系统调用关闭套接字。
int close(int sockfd);3 例:服务端/客户端通信实现
现在通过一个例子来说明Unix套接字的使用。
功能客户端需要一边接收用户的消息并转发给服务端一边接收来自服务端的消息服务端则在建立成功之后对来自客户端的消息进行一个回显。
3.1 服务端
Unix的程序和TCP基本上一样只是这里的地址需要指定一个本地的文件名Unix通过这个文件来进行进程通信。
1、初始化套接字并接受连接
#define SOCKET_PATH /tmp/unix_socket_example
// 创建套接字
server_sock socket(AF_UNIX, SOCK_STREAM, 0);
// 设置服务器地址结构
memset(server_addr, 0, sizeof(server_addr));
server_addr.sun_family AF_UNIX;
strcpy(server_addr.sun_path, SOCKET_PATH);
// 绑定套接字到地址
bind(server_sock, (struct sockaddr*)server_addr, sizeof(server_addr));
// 监听连接
listen(server_sock, 5);
// 接受连接
client_sock accept(server_sock, (struct sockaddr*)client_addr, client_len);这里我们将套接字的地址设置为/tmp/unix_socket_example。
2、接收消息并回显
char buffer[1024];
while (1)
{ssize_t received_bytes recv(client_sock, buffer, sizeof(buffer), 0);if (received_bytes 0){perror(Error receiving message);break;}printf(Received from client: %.*s, (int)received_bytes, buffer);if (send(client_sock, buffer, received_bytes, 0) -1){perror(Error sending message back to client);break;}
}3.2 客户端
1、初始化套接字并连接到服务端
#define SOCKET_PATH /tmp/unix_socket_example
int client_sock;
struct sockaddr_un server_addr;
// 创建套接字
client_sock socket(AF_UNIX, SOCK_STREAM, 0);
// 设置服务器地址结构
memset(server_addr, 0, sizeof(server_addr));
server_addr.sun_family AF_UNIX;
strcpy(server_addr.sun_path, SOCKET_PATH);
// 连接到服务器
connect(client_sock, (struct sockaddr*)server_addr, sizeof(server_addr));2、接收用户输入并发送给服务端
char buffer[1024];
while (1)
{printf(Enter a message: );fgets(buffer, sizeof(buffer), stdin);send(client_sock, buffer, strlen(buffer), 0);ssize_t received_bytes recv(client_sock, buffer, sizeof(buffer), 0);printf(Servers response: %.*s, (int)received_bytes, buffer);
}代码比较简单这里就不使用select或poll等多路I/O复用方式监听用户输入和服务端的消息了大家可以参考我之前的文章实现。
3.3 实验结果
如下图所示服务端收到客户端的消息后再回显给客户端 前面我们的地址设置为/tmp/unix_socket_example也就是说在tmp目录下会创建一个unix_socket_example用于进程通信
开头的s表示套接字文件 现在重启一下服务端提示我们地址已经被使用 我们知道在TCP通信中也有这个问题我们可以设置地址复用
int reuse 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, reuse, sizeof(reuse));但在Unix编程中它是使用文件进行进程通信的所以我们可以在服务端创建之前删除我们这个套接字文件 再来运行一下服务端发现可以创建了 3.4 完整代码
1、服务端
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include sys/un.h#define SOCKET_PATH /tmp/unix_socket_exampleint main() {int server_sock, client_sock;struct sockaddr_un server_addr, client_addr;socklen_t client_len sizeof(client_addr);unlink(SOCKET_PATH);// 创建套接字server_sock socket(AF_UNIX, SOCK_STREAM, 0);if (server_sock -1) {perror(Error creating socket);exit(EXIT_FAILURE);}// 设置服务器地址结构memset(server_addr, 0, sizeof(server_addr));server_addr.sun_family AF_UNIX;strcpy(server_addr.sun_path, SOCKET_PATH);// 绑定套接字到地址if (bind(server_sock, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) {perror(Error binding socket);close(server_sock);exit(EXIT_FAILURE);}// 监听连接if (listen(server_sock, 5) -1) {perror(Error listening for connections);close(server_sock);exit(EXIT_FAILURE);}printf(Server is listening for connections...\n);// 接受连接client_sock accept(server_sock, (struct sockaddr*)client_addr, client_len);if (client_sock -1) {perror(Error accepting connection);close(server_sock);exit(EXIT_FAILURE);}printf(Connection established with a client.\n);// 接收和回显消息char buffer[1024];while (1) {// 接收来自客户端的消息ssize_t received_bytes recv(client_sock, buffer, sizeof(buffer), 0);if (received_bytes 0) {perror(Error receiving message);break;}// 打印接收到的消息printf(Received from client: %.*s, (int)received_bytes, buffer);// 回显消息给客户端if (send(client_sock, buffer, received_bytes, 0) -1) {perror(Error sending message back to client);break;}}// 关闭套接字close(client_sock);close(server_sock);unlink(SOCKET_PATH);return 0;
}2、客户端
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include sys/un.h#define SOCKET_PATH /tmp/unix_socket_exampleint main() {int client_sock;struct sockaddr_un server_addr;// 创建套接字client_sock socket(AF_UNIX, SOCK_STREAM, 0);if (client_sock -1) {perror(Error creating socket);exit(EXIT_FAILURE);}// 设置服务器地址结构memset(server_addr, 0, sizeof(server_addr));server_addr.sun_family AF_UNIX;strcpy(server_addr.sun_path, SOCKET_PATH);// 连接到服务器if (connect(client_sock, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) {perror(Error connecting to server);close(client_sock);exit(EXIT_FAILURE);}printf(Connected to the server.\n);// 与用户交互发送消息给服务器并接收回显消息char buffer[1024];while (1) {printf(Enter a message: );fgets(buffer, sizeof(buffer), stdin);// 发送消息给服务器if (send(client_sock, buffer, strlen(buffer), 0) -1) {perror(Error sending message to server);break;}// 接收来自服务器的回显消息ssize_t received_bytes recv(client_sock, buffer, sizeof(buffer), 0);if (received_bytes 0) {perror(Error receiving message from server);break;}// 打印回显消息printf(Servers response: %.*s, (int)received_bytes, buffer);}// 关闭套接字close(client_sock);return 0;
}4 TCP与Unix中connect的区别
在TCP中如果服务器端的监听队列已满connect()调用会阻塞直到有连接插入为止或者达到连接队列的最大长度。
对于TCP如何非阻塞判断超时可以参考我的另一篇文章I/O系统调用(读/写/连接)的超时处理
而在Unix域套接字编程中当客户端尝试使用connect()系统调用连接到服务器端的Unix域流式套接字时如果服务器端的监听队列已满connect()会立即返回ECONNREFUSED错误。这是因为Unix域套接字使用文件系统路径作为套接字地址而服务器端在处理连接请求时其实是在文件系统中创建一个套接字文件。如果监听队列已满新的连接请求无法被立即处理因此客户端会收到ECONNREFUSED错误。
以下是一些可能引起connect()返回ECONNREFUSED的情况
服务器忙碌 如果服务器端处理连接请求的速度不够快导致监听队列已满客户端可能会收到ECONNREFUSED。并发连接数过多 如果系统中同时存在大量客户端尝试连接到服务器端超过了服务器处理的能力也可能导致监听队列满从而引发ECONNREFUSED。套接字文件权限问题 如果套接字文件所在的目录没有足够的权限可能会导致服务器无法创建新的连接文件从而引发ECONNREFUSED。