Linux Program套接字
Posted Jiamings
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux Program套接字相关的知识,希望对你有一定的参考价值。
系列文章:
- 文件操作
- 数据管理
- 进程和信号
- POSIX 线程
- 进程间通信:管道
- 信号量共享内存和消息队列
- 套接字
文章目录
- 2.1 套接字属性
- 2.2 创建套接字
- 2.3 套接字地址
- 2.4 命名套接字
- 2.5 创建套接字队列
- 2.6 接受连接
- 2.7 请求连接
- 2.8 关闭套接字
之前所讨论的进程间通信的方法均是基于一台计算机系统的共享资源实现,这里的资源可以是文件系统空间、共享的物理内存或消息队列,但只有运行在同一台机器上的进程才能够使用它们。
一台机器上的进程可以使用套接字和另外一台机器上的进程通信,这样就可以支持分布在网络中的客户/服务器系统。同一台机器上的进程之间也可以使用套接字进行通信。
- 套接字连接的工作原理
- 套接字的属性、地址和通信
- 网络信息和互联网守护进程
- 客户和服务器
1. 什么是套接字
socket 是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行,Linux 所提供的功能(如打印服务、连接数据库和提供Web页面)和网络工具(rlogin和ftp)通常都是通过套接字来进行通信的。
2. 套接字连接
套接字应用程序是如何通过套接字来维持一个连接的。
首先,服务器应用程序用系统调用 socket 来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其它进程共享。
接下来,服务器进程会给套接字起个名字,本地套接字的名字是 Linux 文件系统中的文件名,一般放在 /tmp 或 /usr/tmp 目录中,对于网络套接字,它的名字与客户连接的特定网络有关的服务标志符(端口号或访问点),这个标识符允许Linux将进入的针对特定端口号的连接转到正确的服务器进程。
例如,Web服务器一般在80端口上创建一个套接字,这是一个专用于此目的的标识符,Web浏览器知道对于用户想要访问的Web站点,应该使用端口80来建立HTTP连接。我们用系统调用bind来给套接字命名,然后服务器进程就开始等待客户连接到这个命名套接字。系统调用 listen 的作用是,创建一个队列并将其用于存放来自客户的进入连接,服务器通过系统调用 accept 来接受客户的连接。
服务器调用 accept 时,它会创建一个与原有的命名套接字不同的新的套接字。这个新套接字只用于与这个特定的客户进行通信,而命名套接字则被保留下来继续处理来自其它客户的连接。如果服务器编写得当,它就可以充分利用多个连接带来的好处。Web服务器就会这么做以同时服务来自许多客户的页面请求。对于一个简单的服务器来说,后续的客户将在监听队列中等待,直到服务器再次准备就绪。
基于套接字系统的客户端增加简单,客户首先调用 socket 创建一个未命名套接字,然后将服务器的命名套接字作为一个地址来调用 connect 与服务器建立连接。
一旦连接建立,就可以像使用底层的文件描述符那样用套接字实现双向的数据通信。
本地客户
int main()
int sockfd;
int len;
struct sockaddr_un address;
int result;
char ch = A;
// 为客户创建一个套接字
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
// 根据服务器的情况给套接字命名
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
len = sizeof(address);
// 将套接字连接到服务器的套接字
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1)
perror("oops: client1");
exit(1);
// 通过sockfd进行读写操作
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
printf("char from server = %c", ch);
close(sockfd);
exit(0);
int main()
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
// 删除以前的套接字,为服务器建立一个未命名的套接字
unlink("server_socket");
server_sockfd = 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_sockfd, (struct sockaddr *)&server_address, server_len);
// 创建一个连接队列,开始等待客户进行连接
listen(server_sockfd, 5);
while (1)
char ch;
printf("server waiting\\n");
// 接受一个连接
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
// 对client_sockfd套接字上的客户进行读写操作
read(client_sockfd, &ch, 1);
ch++;
write(client_sockfd, &ch, 1);
close(client_sockfd);
这个例子中的服务器程序一次只能为一个客户服务,它从客户那里读取一个字符,增加它的值,然后再把它写回去。
jiaming@jiaming-pc:~/Documents/test$ cc client1.c -o client
jiaming@jiaming-pc:~/Documents/test$ cc client1.c -o client1
jiaming@jiaming-pc:~/Documents/test$ ./client1
oops: client1: No such file or directory
jiaming@jiaming-pc:~/Documents/tes
jiaming@jiaming-pc:~/Documents/test$ cc server1.c -o server
jiaming@jiaming-pc:~/Documents/test$ ./server &
[1] 17944
jiaming@jiaming-pc:~/Documents/test$ server waiting
jiaming@jiaming-pc:~/Documents/test$ ls -lF server_socket
srwxrwxr-x 1 jiaming jiaming 0 5月 29 17:23 server_socket=
jiaming@jiaming-pc:~/Documents/test$ ps lx
0 1000 17944 7243 20 0 2500 576 skb_wa S pts/1 0:00 ./server
4 1000 18051 7243 20 0 14496 3436 - R+ pts/1 0:00 ps lx
jiaming@jiaming-pc:~/Documents/test$ cc client1.c -o client1
jiaming@jiaming-pc:~/Documents/test$ ./client1
server waiting
char from server = Bjiaming@jiaming-pc:~/Documents/test$
jiaming@jiaming-pc:~/Documents/test$ ./client1 & ./client1 & ./client1 &
[2] 18297
[3] 18298
[4] 18299
jiaming@jiaming-pc:~/Documents/test$ server waiting
server waiting
char from server = Bserver waiting
char from server = Bchar from server = B
[2] Done ./client1
[3]- Done ./client1
[4]+ Done ./client1
2.1 套接字属性
套接字的特性由3个属性确定,域、类型和协议。
1. 套接字的域
域指定套接字通信中使用的网络介质,最常见的套接字域是 AF_INET,它指的是Internet网络,许多 Linux 局域网使用的都是该网络,当然因特网自身用的也是它,其底层的协议 —— 网际协议(IP)只有一个地址簇,它使用一种特定的方式来指定网络中的计算机,IP地址。
服务器计算机上可能同时有多个服务正在运行,客户可以通过 IP 端口来指定一台联网机器上的某个特定服务。在系统内部,端口通过分配一个唯一的16位的整数来标识,在系统的外部,则需要通过 IP 地址和端口号的组合来确定,套接字作为通信的终点,它必须在开始通信前绑定一个端口。
服务器在特定的端口等待客户的连接,一般情况下小于1024的端口号都是为系统服务保留的,并且所服务的进程必须具有超级用户权限。
因为标准服务都对应标准的端口号,所以计算机之间可以轻松地互连,而不需要首先协商一个正确的端口号,本地服务可以使用非标准的端口地址。
例子中的域是UNIX文件系统域AF_UNIX,即使是一台还未联网的计算机上的套接字也可以使用这个域。这个域的底层协议就是文件输入输出,而它的地址就是文件名。我们的服务套接字的地址是server_socket,当我们运行服务器程序时,就可以在当前目录下看到这个地址。
2. 套接字类型
一个套接字域可能有很多种不同的通信方式,而每种通信方式又有其不同的特性,但是 AF_UNIX 域的套接字没有这样的问题,它们提供了一个可靠的双向通信路径。在网络域中,我们需要注意底层网络的特性,以及不同的通信机制是如何受到它们的影响的。
因特网协议提供了两种通信机制:流(stream)和数据报(datagram),它们有着截然不同的服务层次。
- 流套接字
流套接字(在某些方面类似与标准的输入/输出流)提供的一个有序、可靠、双向字节流的连接。因此,发送的数据可以确保不会丢失、复制或乱序到达,并且在这一过程中发生的错误也不会显示出来。大的消息被分片、传输、再重组。
流套接字由类型 SOCK_STREAM 指定,它们是在 AF_INET 域中通过 TCP/IP 连接实现的,它们也是AF_UNIX域中常用的套接字类型。
- 数据报套接字
与流套接字相反,由类型 SOCK_DGRAM 指定的数据报套接字不建立和维持一个连接。它对可以发送的数据报的长度有限制,数据报作为一个单独的网络消息被传输,它可能丢失、复制或乱序到达。
数据报套接字是在 AF_INET 域中通过 UDP/IP 连接实现的,它提供一个无序的不可靠服务,但从资源的角度来看,相对来说它们开销比较小,因为不需要维持网络连接,而且因为无需花费时间来建立连接,所以它们的速度也很快。
数据报适用于信息服务中的单次查询,它主要用来提供日常状态信息或执行低优先级的日志记录,它的优点是服务器的奔溃不会给客户造成不变,也不会要求客户重启,因为基于数据报的服务器通常不保留连接信息,所以它们可以在不打扰其它客户的前提下停止并重启。
2.2 创建套接字
socket 系统调用创建一个套接字,并返回一个描述符:
int socket(int domain, int type, int protocol);
创建的套接字是一条通信线路的一个端点,domain 参数指定协议簇,type 参数指定这个套接字的通信类型,protocol 参数指定使用的协议。
domain:
AP_UNIX: UNIX 域协议,文件系统套接字
AP_INET: ARPA因特网协议,网络套接字
AP_ISO: ISO 标准协议
AP_NS: 施乐网络系统协议
AP_IPX: Novell IPX 协议
AP_APPLETALK: Appletalk DDS
type: SOCK_STREAM、SOCK_DGRAM。
SOCK_STREAM: 是一个有序、可靠、面向连接的双向字节流,对 AF_INET 域套接字来说,它默认是通过一个 TCP 连接来提供这一特性的,TCP 连接在两个流套接字端点之间建立。数据可以通过套接字连接进行双向传递。TCP 协议所提供的机制可以用于分片和重组长消息,并且可以重传可能在网络中丢失的数据。
SOCK_DGRAM 是数据报服务。可以用它来发送最大长度固定的消息,对于 AF_INET 域套接字来说,这种类型的通信是由 UDP 数据报来提供的。
通信所用的协议一般由套接字类型和套接字域来决定,通常不需要选择,只有当需要选择时,才用到 protocol参数,将该参数设置为 0 表示使用默认协议。
socket 系统调用返回一个描述符,它在许多方面类似于底层的文件描述符。当这个套接字连接到另一端的套接字后,就可以用read和write系统调用,通过这个描述符来在套接字上发送和接收数据,close系统调用用于结束套接字连接。
2.3 套接字地址
每个套接字域都有自己的地址格式,对于 AF_UNIX 域套接字来说,它的地址由结构 sockaddr_un 来描述,该结构定义在头文件 sys/un.h中。
struct sockaddr_un
sa_family_t sun_family; // AF_UNIX
char sun_path[]; // pathname
因此,对套接字进行处理的系统调用可能需要接受不同类型的地址,每种地址格式都使用一种类似的结构来描述,它们都以一个指定地址类型(套接字域)的成员开始。
sun_path 指定的路径名长度也是有限制的,linux规定 108 个字符。
在 AF_INET 域中,套接字地址由结构 sockaddr_in 来指定,该结构定义在头文件 netinet/in.h中,至少包含以下几个成员:
struct sockaddr_in
short in sin_family; // AF_INET
unsigned short int sin_port; Linux Program数据管理