Socket套接字

不论是pipe还是fifo,它们在进程进程间通信时都有资源共享,基本都只能运行在同一台机器上才行,而想要一台机器上的进程和另一台机器上的进程进行数据通信,可以用到socket套接字接口,除了可以与管道相似的方法使用socket之外,套接字还包括了计算机网络中的通信,多台机器进程之间进行通信,更直白一点可以支持网络中的客户端/服务器通信,可是一台机器不同的进程可以通过进程PID来标识区分,网络中却不行,但是根据TCP/IP协议,网络层的IP Addr可以标识网络中不同的主机,传输层的port+protocol足以标识主机中的程序进程了,这样网络中的进程间通信可以通过这三者ip+port+protocol标识一个进程,进而相互进行通信

套接字是通信端点的抽象,与用文件描述符访问文件一样,访问套接字需要套接字描述符,实际上也是用文件描述符来实现的

套接字应用程序维持一个连接:

首先服务器应用程序通过系统调用socket创建一个套接字,它仅仅是系统分配给服务器端进程的文件描述符资源,不能与其它进程共享

然后服务器会给套接字命名,本地套接字的名字也就是linux文件系统的文件名,而如果是网络套接字,它的名字是与客户端连接的特定网络有关的服务标识符,这个标识符允许linux将进入的针对特定端口号的连接转到正确的服务进程中;比如web服务器一般会在80端口上创建一个套接字,而web浏览器对于用户想要访问web站点,就使用80端口来建立HTTP连接,通过系统调用bind来给套接字命名,服务器进程开始等待客户连接到这个命名套接字,系统调用listen作用是创建一个队列并将其用于存放来自客户的连接,accept来接受客户的连接

服务器调用accept会创建一个与原有的命名套接字不同的新套接字,这个新套接字只用于与这个特定的客户进行通信,而命名套接字则被保留下来继续处理来自其它客户的连接;如果服务器编写得当,可以充分利用多个连接带来的好处;web服务器可以这样来处理同时来自不同客户的web请求,而简单的服务器来说,可能后续客户请求都在监听队列中等待,直到服务器再次准备就绪

基于套接字系统的客户端比较简单,首先调用scoket创建一个未命名的套接字,然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接

建立了连接之后,就可以类似使用底层的文件描述符来用套接字进行双向数据通信

下面是一个简单的本地socket通信:

server端

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

int main(){
    int server_socket_fd, client_socket_fd;
    int client_len, server_len;
    struct sockaddr_un server_address;
    struct sockaddr_un client_address;

    unlink("server_socket");
    server_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);

    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "server_socket");
    server_len = sizeof(server_address);
    bind(server_socket_fd, (struct sockaddr *)&server_address, server_len);

    listen(server_socket_fd, 5);
    while(1){
        char ch;
        printf("server waiting\n");

        client_len = sizeof(client_address);
        client_socket_fd = accept(server_socket_fd, (struct sockaddr *)&client_address, &client_len);

        read(client_socket_fd, &ch, 1);
        ++ch;
        write(client_socket_fd, &ch, 1);
        close(client_socket_fd);
    }

    return 0;
}

client端

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

int main(){
    int socket_fd;
    struct sockaddr_un address;
    char ch = 'A';

    socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, "server_scoket");

    if (connect(socket_fd, (struct sockaddr *)&address, sizeof(address)) == -1){
        perror("client connect failed!");
        exit(1);
    }

    write(socket_fd, &ch, 1);
    read(socket_fd, &ch, 1);
    printf("char from server is: %c\n", ch);
    close(socket_fd);

    return 0;
}

这个小程序中服务器程序一次只能为一个客户端服务,从客户端取一个字符,增加它的值,然后把它歇会去

编译他们:

lihui@2015 $ cc -o client client.c

lihui@2015 $ cc -o server server.c

先运行服务端,会等待中

lihui@2015 $ ./server.exe
server waiting
光标

此时套接字已经创建,可以查看一下:

lihui@2015 $ ls -lF server_socket
srwxr-xr-x 1 lihui None 0 二月  1 01:32 server_socket=

最前面的s和末尾的=表明该文件类型是套接字;在用完一个套接字之后,应该把它删除掉,即使是在程序因接收到一个信号而异常终止的情况下也要这么做,避免文件系统充斥着无用的文件而变得十分混乱

下面重新开一个终端,启动客户端程序,就可以连接上服务器了,由于服务器套接字已经存在了,可以连接到它并与服务器进行通信

lihui@2015 $ ./client.exe
client connect failed!: No such file or directory

居然还是报错,找不到套接字文件,可是显然此刻是已经创建好了的,这里很奇怪,有空再找找原因~!

发表回复