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