在学习Linux系统里的套接字连接是如何建立起来之前,我们需要先了解套接字应用程序是如何通过套接字来维持一个连接的。
首先,服务器应用程序必须先创建出一个套接字,这是分配给该服务器进程的一个操作系统资源,因为这个套接字是由该服务器通过系统调用 socket 创建出来的,所以其他进程将不能对它进行访问。
接着,服务器进程会给套接字起个名字。给本地套接字的名字是Linux文件系统中的一个文件名,一般放在/tmp或/usr/tmp子目录里。而网络套接字的名字则是一个与客户所能连接的特定网络有关的服务标识符(也叫做端口号或访问点)。给套接字起名字(这个操作叫做“绑定”)要使用系统调用bind。然后,服务器就开始等待有客户连接到这个命名套接字上来。系统调用listen的作用是创建一个队列,来自客户的连接将在这个队列上排队等待服务器的处理(这个过程叫做“监听”)。服务器将通过系统调用accept来接受来自客户的接入连接。
当服务器调用accept的时候,会新创建一个套接字,这个套接字与刚才说的命名套接字不是一回事。新套接字的唯一用途就是与这个特定的客户进行通信,而命名套接字则被解放出来,准备处理来自其他客户的连接。如果服务器编写得当,就可以享受多个连接带来的好处。对一个简单的服务器来说,后来的客户需要在队列里等待服务器的重新就绪。
基于套接字系统的客户端就比较简单了。客户先通过调用socket创建出一个未命名套接字,然后调用connect利用服务器的命名套接字和一个地址来建立起一个连接。套接字被建立起来之后,人们就可以像对待底层文件描述符那样用它来实现双向的数据通信了。
下面是一个简单的示范程序,如有需要可以扩展其功能:
- /* server.cc */
- #include <cstdio>
- #include <cstdlib>
- #include "sys/socket.h"
- #include "netinet/in.h"
- #include "unistd.h"
- #define SRV_PORT 6500
- using namespace std;
- char buffer[512];
- int main(int argc, char *argv[])
- {
- /* Create a TCP socket */
- int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
- /* Construct address */
- struct sockaddr_in server_address;
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = htonl(INADDR_ANY);
- server_address.sin_port = htons(SRV_PORT);
- int len = sizeof(server_address);
- /* Bind address to socket and listen */
- bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address));
- listen(server_sockfd, 5);
- /* Serving */
- while(1) {
- /* Accept */
- struct sockaddr client_address;
- int client_len = sizeof(client_address);
- int client_sockfd = accept(server_sockfd,
- (struct sockaddr *)&client_address,
- (socklen_t *)&client_address);
- /* Read */
- read(client_sockfd, buffer, sizeof(buffer));
- /* Handle */
- sprintf(buffer, "i am server");
- /* Write */
- write(client_sockfd, buffer, sizeof(buffer));
- /* Close */
- close(client_sockfd);
- }
- /* Close */
- close(server_sockfd);
- return 0;
- }
- /* client.cc */
- #include <iostream>
- #include <cstdio>
- #include <cstdlib>
- #include "arpa/inet.h"
- #include "sys/socket.h"
- #include "netinet/in.h"
- #include "unistd.h"
- #define SRV_ADDR "127.0.0.1"
- #define SRV_PORT 5432
- using namespace std;
- char buffer[512];
- int main(int argc, char *argv[])
- {
- /* Create a TCP socket */
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- /* Construct address */
- struct sockaddr_in address;
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = inet_addr(SRV_ADDR);
- address.sin_port = htons(SRV_PORT);
- int len = sizeof(address);
- /* Connect */
- connect(sockfd, (struct sockaddr *)&address, len);
- /* Write */
- write(sockfd, buffer, sizeof(buffer));
- /* Read */
- read(sockfd, buffer, sizeof(buffer));
- /* Debug */
- cout << buffer << endl;
- /* close */
- close(sockfd);
- return 0;
- }