套接字
1.什么是套接字?
套接字(socket)是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。
2.套接字应用程序是如何通过套接字来维持一个连接的?
首先,服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其它进程共享。
接下来,服务器进程会给套接字起个名字。本地套接字的名字是Linux文件系统中的文件名,一般放在/temp或/user/tmp目录中。对于网络套接字,它的名字是与客户连接的
特定网络有关的服务标识符(端口号或访问点)。这个标识符允许Linux将进入的针对特定端口号的连接转到正确的服务器进程。
例如,Web服务器一般在80端口上创建一个套接字,这是一个专门用于次目的的标识符。Web浏览器知道对于用户想要访问的Web站点,应该使用端口80来建立HTTP连接。
我们用系统调用bind来给套接字命令。然后服务器进程就开始等待客户连接到这个命名套接字。系统调用listen的作用是,创建一个队列并将其用于存放来自客户的进入连接。
服务器通过系统调用accept来接受客户的连接。
服务器调用accept时,它会创建一个与原有的命名套接字不同的新的套接字。这个新套接字只用于与这个特定的客户进行通信,而命名套接字则被保留下来继续处理来自其他客户的连接。
如果服务器编写的当,它就可以充分利用多个连接带来的好处。Web服务器就会这么做以同时服务来自许多客户的页面请求。对一个简单的服务器来说,后续的客户将在监听队列中等待,
直到服务器再次准备就绪。
而基于套接字系统的客户端更简单。客户首先调动socket创建一个未命名套接字,然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。
一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向的数据通信。
3.套接字属性
套接字的属性由三个属性确定,即域(demain) 、类型(type)、协议(protocol)。
域:指定套接字通信中使用的网络介质。最常见的套接字域是AF_INET,它指的是Internet网络,许多Linux局域网使用的都是该网络,当然,因特网自身用的也是它。
类型:因特网协议提供了两种通信机制:流(stream)和数据报(datagram).
流套接字提供的是一个有序、可靠、双向字节流的连接。 流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现的。
数据报套接字由类型SOCK_DGRAM指定的,不建立和维持一个连接。它对发送的数据报的长度有限制。数据报作为一个单独的网络消息被传输,它可能会丢失、复制或乱序到达。
协议:现在先不需要考虑。。。(╯—﹏—)╯( ┷━━━┷
干货:
创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol)
socket系统调用创建回一个套接字并返回一个描述符,该描述符可以访问该套接字。创建的套接字是一条通信线路的一个端点。各参数就是上面提的三个属性。
套接字的地址
在AF_INET域中,套接字地址由结构体sockaddr_in来指定,该结构体的定义在头文件netinet/in.h中,它至少包含以下成员:
struct sockaddr_in{
short int sin_family; /* AF_INET*/
unsigned short int sin_port;
struct in_addr sin_addr;
}
struct in_addr{
unsigned long int s_addr;
}
命名套接字
要想让通过socket调用创建的套接字可以被其他进程使用,服务器程序就必须给该套接字命名。这样,AF_INET套接字就会关联到一个IP端口号
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *addrss, size_t address_len)
bind系统把参数address中的地址分配给文件描述符socket关联的未命名套接字。地址的长度由address_len传递。bind调用需要将一个特定的地址结构指针装换为指向
通用地址类型。
创建套接字队列
为了能够在套接字上接受进入的连接,服务器程序必须创建一个队列来保存未处理的请求,它用listen系统调用来完成这一工作。
#include <sys/socket.h>
int listen(int socket, int backlog)
Linux系统可能会对队列中可以容纳的未处理连接的最大化数目作出限制。将这个队列长度设为backLog的值。在套接字队列中,等待处理的进入连接的个数最多不能超过这个数字。
再往后的连接将被中断,导致客户的连接请求失败。
接受连接
一旦服务器程序创建并命名了套接字之后,他就可以通过accept系统调用来等待客户建立对该套接字的连接。
#include <sys/socket.h>
int accept(int socket, struct sockaddr *address, size_t *address_len)
accept系统调用只有当有客户程序试图连接到有socket参数指定的套接字上时才返回。这里的客户是指,在套接字队列中排在第一个的未处理的连接。accept函数将创建一个新套接字来与该客户进行
通信,并且返回新套接字的描述符。新套接字的类型和服务器监听套接字类型是一样的。
请求连接
客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来来连接到服务器。
#include <sys/socket.h>
int connect(int socket, struct sockaddr *address, size_t *address_len)
成功返回0,失败时返回-1
关闭套接字
close(int socket);
主机字节序和网络字节序
为了使不同类型的计算机可以就通过网络传输的多字节整数的值达到一致,你需要定义一个网络字节序。客户和服务器程序必须在传输之前,将它们内部的整数表达方式转换为
网络字节序。它们通过定义在头文件netinet/in.h中的函数来完成这一工作,
htonl(),hrons(),ntohl(),ntohs() host to network long 长整数从主机字节序到网络字节序的转换。
服务器程序:
客户端:
执行效果: