📜  Unix套接字-服务器示例(1)

📅  最后修改于: 2023-12-03 15:05:45.900000             🧑  作者: Mango

Unix套接字-服务器示例

Unix套接字是一种通信机制,用于进程间的通信。Unix套接字是基于文件描述符的,因此可以在一个进程中同时处理多个套接字。通过Unix套接字,我们可以实现本地进程之间的通信,也可以实现网络进程之间的通信。

在Unix系统上,我们可以通过套接字来实现一个简单的服务器。下面是一个使用Unix套接字实现的服务器示例。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCK_PATH "/tmp/sock_path"

int main()
{
    int s, s2, t, len;
    struct sockaddr_un remote, server;

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        fprintf(stderr, "socket error: %s\n", strerror(errno));
        exit(1);
    }

    printf("Trying to bind to %s\n", SOCK_PATH);

    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, SOCK_PATH);
    unlink(server.sun_path);
    len = strlen(server.sun_path) + sizeof(server.sun_family);

    if (bind(s, (struct sockaddr *)&server, len) == -1) {
        fprintf(stderr, "bind error: %s\n", strerror(errno));
        exit(1);
    }

    if (listen(s, 5) == -1) {
        fprintf(stderr, "listen error: %s\n", strerror(errno));
        exit(1);
    }

    while(1) {
        int done, n;
        printf("Waiting for a connection...\n");
        t = sizeof(remote);

        if ((s2 = accept(s, (struct sockaddr *)&remote, (socklen_t *)&t)) == -1) {
            fprintf(stderr, "accept error: %s\n", strerror(errno));
            exit(1);
        }

        printf("Connection accepted.\n");

        do {
            n = recv(s2, buf, sizeof(buf), 0);
            if (n <= 0) {
                if (n < 0) {
                    fprintf(stderr, "recv error: %s\n", strerror(errno));
                }
                done = 1;
            }
            if (!done) {
                if (send(s2, buf, n, 0) < 0) {
                    fprintf(stderr, "send error: %s\n", strerror(errno));
                    done = 1;
                }
            }
        } while (!done);
        close(s2);
    }

    return 0;
}
代码分析
socket()

创建一个Unix域套接字,返回文件描述符。

if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
    fprintf(stderr, "socket error: %s\n", strerror(errno));
    exit(1);
}
bind()

将服务器套接字与一个地址绑定,以便客户端可以找到它。

if (bind(s, (struct sockaddr *)&server, len) == -1) {
    fprintf(stderr, "bind error: %s\n", strerror(errno));
    exit(1);
}
listen()

将套接字设置为监听状态,以便客户端可以连接它。

if (listen(s, 5) == -1) {
    fprintf(stderr, "listen error: %s\n", strerror(errno));
    exit(1);
}
accept()

等待客户端的连接请求,接受客户端的连接请求,返回新的文件描述符。

if ((s2 = accept(s, (struct sockaddr *)&remote, (socklen_t *)&t)) == -1) {
    fprintf(stderr, "accept error: %s\n", strerror(errno));
    exit(1);
}
recv()、send()

从客户端接收数据,向客户端发送数据。

do {
    n = recv(s2, buf, sizeof(buf), 0);
    if (n <= 0) {
        if (n < 0) {
            fprintf(stderr, "recv error: %s\n", strerror(errno));
        }
        done = 1;
    }
    if (!done) {
        if (send(s2, buf, n, 0) < 0) {
            fprintf(stderr, "send error: %s\n", strerror(errno));
            done = 1;
        }
    }
} while (!done);
运行程序

编译服务器程序:

$ gcc server.c -o server

在终端窗口1中运行服务器程序:

$ ./server
Trying to bind to /tmp/sock_path
Waiting for a connection...

在终端窗口2中运行客户端程序:

$ gcc client.c -o client
$ ./client
Connected.

在终端窗口1中可以看到服务器程序输出了:

Connection accepted.

这说明客户端已经成功地连接到了服务器端。在终端窗口2中输入一些文字,可以看到回显。这表明客户端已经成功地向服务器发送了数据,服务器也成功地回显了这些数据。