目录
1.什么是套接字?
套接字允许在相同或不同机器上的两个不同进程之间进行通信。更准确地说,这是使用标准Unix文件描述符与其他计算机进行通讯的一种方式。在Unix中,每个I / O操作都是通过写入或读取文件描述符来完成的。文件描述符只是与打开的文件关联的整数,它可以是网络连接,文本文件,终端或其他内容。
对于程序员来说,套接字的外观和行为都非常类似于低级文件描述符。这是因为read()和write()之类的命令与套接字一起使用的方式与对文件和管道的处理方式相同。
套接字首先在2.1BSD中引入,然后在4.2BSD中完善为当前形式。现在,大多数最新的UNIX系统版本都提供了套接字功能。
套接字在哪里使用?
客户端服务器应用程序框架中使用Unix套接字。服务器是根据客户端请求执行某些功能的过程。大多数应用程序级协议(例如FTP,SMTP和POP3)都利用套接字在客户端和服务器之间建立连接,然后交换数据。
套接字类型
用户可以使用四种类型的插座。前两个是最常用的,而后两个则很少使用。
假定进程仅在相同类型的套接字之间进行通信,但是没有限制可以防止不同类型的套接字之间进行通信。
流套接字 -保证在网络环境中的交付。如果通过流套接字发送三个项目“ A,B,C”,它们将以相同的顺序到达-“ A,B,C”。这些套接字使用TCP(传输控制协议)进行数据传输。如果无法传送,则发件人会收到错误指示。数据记录没有任何边界。
数据报套接字 -无法保证在网络环境中的传递。它们是无连接的,因为您不需要像Stream Sockets中那样具有开放的连接-您可以使用目标信息构建数据包并将其发送出去。他们使用UDP(用户数据报协议)。
原始套接字(Raw Sockets) -这些套接字使用户可以访问支持套接字抽象的底层通信协议。这些套接字通常是面向数据报的,尽管它们的确切特性取决于协议提供的接口。原始套接字不适合一般用户使用;它们主要是为那些对开发新的通信协议感兴趣的人,或为获得对现有协议中一些更隐秘的功能的访问而提供的。
顺序数据包套接字 -它们类似于流套接字,但保留记录边界。该接口仅作为网络系统(NS)套接字抽象的一部分提供,并且在大多数严肃的NS应用程序中非常重要。顺序数据包套接字允许用户通过编写原型头以及要发送的任何数据来操作数据包或一组数据包上的序列数据包协议(SPP)或Internet数据报协议(IDP)标头指定将与所有传出数据一起使用的默认标头,并允许用户接收传入数据包上的标头。
下一步是什么?
接下来的几章旨在增强基础知识并准备基础,然后才能使用套接字编写Server和Client程序。如果您直接想要跳转以查看如何编写客户端和服务器程序,则可以这样做,但是不建议这样做。强烈建议您循序渐进地完成这些最初的几章,以便在继续进行编程之前奠定基础。
2.摘要
这是与套接字编程有关的所有功能的列表。
端口和服务功能
Unix提供以下功能来从/ etc / services文件中获取服务名称。
struct servent * getservbyname(char * name,char * proto) -此调用采用服务名称和协议名称,并返回该服务的相应端口号。
struct servent * getservbyport(int port,char * proto) -此调用使用端口号和协议名称,并返回相应的服务名称。
字节排序功能
unsigned short htons(unsigned short hostshort) -此函数将16位(2字节)数量的数据从主机字节顺序转换为网络字节顺序。
unsigned long htonl(unsigned long hostlong) -此函数将32位(4字节)数量从主机字节顺序转换为网络字节顺序。
unsigned short ntohs(unsigned short netshort) -此函数将16位(2字节)数量从网络字节顺序转换为主机字节顺序。
unsigned long ntohl(无符号长netlong) -此函数将32位数量从网络字节顺序转换为主机字节顺序。
IP地址功能
int inet_aton(const char * strptr,struct in_addr * addrptr) -此函数调用将以Internet标准点表示法将指定的字符串转换为网络地址,并将该地址存储在提供的结构中。转换后的地址将按网络字节顺序(字节从左到右顺序)。如果字符串有效,则返回1,错误则返回0。
in_addr_t inet_addr(const char * strptr) -此函数调用将以Internet标准点表示法将指定的字符串转换为适合用作Internet地址的整数值。转换后的地址将按网络字节顺序(字节从左到右顺序)。它返回32位二进制网络字节排序的IPv4地址,并在出错时返回INADDR_NONE。
char * inet_ntoa(struct in_addr inaddr) -此函数调用将指定的Internet主机地址转换为Internet标准点表示法中的字符串。
套接字核心功能
int套接字(int家族,int类型,int协议) -此调用返回一个套接字描述符,您可以在以后的系统调用中使用它,或者在错误时返回-1。
int connect(int sockfd,struct sockaddr * serv_addr,int addrlen) -TCP客户端使用connect函数建立与TCP服务器的连接。如果成功连接到服务器,则此调用返回0,否则返回-1。
int bind(int sockfd,struct sockaddr * my_addr,int addrlen) -绑定函数将本地协议地址分配给套接字。如果成功绑定到该地址,则此调用返回0,否则返回-1。
int listen(int sockfd,int backlog) -仅由TCP服务器调用listen函数以侦听客户端请求。该调用成功返回0,否则返回-1。
int accept(int sockfd,struct sockaddr * cliaddr,socklen_t * addrlen) -TCP服务器调用accept函数来接受客户端请求并建立实际连接。成功时此调用返回非负描述符,否则返回-1。
int send(int sockfd,const void * msg,int len,int标志)-send函数用于通过流套接字或CONNECTED数据报套接字发送数据。该调用返回发送的字节数,否则返回-1。
int recv(int sockfd,void * buf,int len,无符号int标志)-recv函数用于通过流套接字或CONNECTED数据报套接字接收数据。该调用返回读入缓冲区的字节数,否则返回-1。
int sendto(int sockfd,const void * msg,int len,无符号int标志,const struct sockaddr * to,int tolen)-sendto函数用于通过UNCONNECTED数据报套接字发送数据。此调用返回发送的字节数,否则返回-1。
int recvfrom(int sockfd,void * buf,int len,无符号int标志struct sockaddr * from,int * fromlen)-recvfrom函数用于从未连接的数据报套接字接收数据。该调用返回读入缓冲区的字节数,否则返回-1。
int close(int sockfd)-close函数用于关闭客户端和服务器之间的通信。该调用成功返回0,否则返回-1。
int shutdown(int sockfd,int how) -关闭功能用于正常关闭客户端和服务器之间的通信。与关闭功能相比,此功能可提供更多控制。成功返回0,否则返回-1。
int select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * errorfds,struct timeval * timeout) -此函数用于读取或写入多个套接字。
套接字助手功能
int write(int fildes,const void * buf,int nbyte) -写入函数尝试将buf指向的缓冲区中的nbyte字节写入与打开的文件描述符fildes相关的文件。成功完成后,write()返回实际写入与fildes关联的文件的字节数。此数字永远不会大于nbyte。否则,返回-1。
int read(int fildes,const void * buf,int nbyte) -读取函数尝试从与打开的文件描述符fildes相关的文件中将nbyte字节读取到buf指向的缓冲区中。成功完成后,write()返回实际写入与fildes关联的文件的字节数。此数字永远不会大于nbyte。否则,返回-1。
int fork(void) -fork函数创建一个新进程。新进程称为子进程,将是调用进程(父进程)的精确副本。
void bzero(void * s,int nbyte) -bzero函数将nbyte空字节放入字符串s中。此函数将用于将所有套接字结构设置为空值。
int bcmp(const void * s1,const void * s2,int nbyte) -bcmp函数将字节字符串s1与字节字符串s2进行比较。假定两个字符串的长度均为nbyte个字节。
void bcopy(const void * s1,void * s2,int nbyte) -bcopy函数将nbyte个字节从字符串s1复制到字符串s2。重叠的字符串处理正确。
void * memset(void * s,int c,int nbyte)-memset函数也用于以bzero相同的方式设置结构变量。
3.网络地址
在继续实际工作之前,让我们先讨论一下网络地址-IP地址。
IP主机地址,或更常见的是IP地址,用于标识连接到Internet的主机。IP代表Internet协议,是指Internet整个网络体系结构的Internet层。
IP地址是32位数字,可解释为四个8位数字或八位字节。每个IP地址唯一地标识参与的用户网络,网络上的主机以及用户网络的类别。
IP地址通常以点分十进制的形式写成N1.N2.N3.N4,其中每个Ni是介于0和255之间的十进制数字(00到FF十六进制)。
地址类别
IP地址由Internet号码分配机构(IANA)管理和创建。有五种不同的地址类别。您可以通过检查IP地址的前四位来确定IP地址属于哪个类。
A类地址以0xxx或十进制1到126开头。
B类地址以10xx或十进制128到191开头。
C类地址以110x或十进制192至223开头。
D类地址以1110或十进制224至239开头。
E类地址以1111或十进制240到254开头。
以01111111开头或十进制127开头的地址保留用于环回和本地计算机上的内部测试[您可以测试一下:您应该始终能够ping 127.0.0.1(指向您自己)];D类地址保留用于多播;E类地址保留供将来使用。它们不应用作主机地址。
例
类 | 最左边的位 | 起始地址 | 完成地址 |
A | 0xxx | 0.0.0.0 | 127.255.255.255 |
B | 10xx | 128.0.0.0 | 191.255.255.255 |
C | 110倍 | 192.0.0.0 | 223.255.255.255 |
D | 1110 | 224.0.0.0 | 239.255.255.255 |
E | 1111 | 240.0.0.0 | 255.255.255.255 |
子网划分
子网划分或子网划分基本上是指分支网络。可以出于多种原因完成此操作,例如组织中的网络,使用不同的物理媒体(例如以太网,FDDI,WAN等),保留地址空间和安全性。最常见的原因是控制网络流量。
子网划分的基本思想是将IP地址的主机标识符部分分为两部分-
- 网络地址本身内的一个子网地址;和
- 子网中的主机地址。
例如,常见的B类地址格式为N1.N2.SH,其中N1.N2标识B类网络,8位S字段标识子网,而8位H字段标识子网中的主机。
4.网络主机名
用数字表示的主机名很难记住,因此用普通名称(例如Takshila或Nalanda)来命名。我们编写软件应用程序以查找与给定名称对应的点分IP地址。
根据给定的字母数字主机名找出点分IP地址的过程称为主机名解析。
主机名解析是通过高容量系统上的特殊软件完成的。这些系统称为域名系统(DNS),它保留IP地址和对应的普通名称的映射。
/ etc / hosts文件
主机名和IP地址之间的对应关系保存在名为hosts的文件中。在大多数系统上,此文件位于/ etc目录中。
该文件中的条目如下所示:
# This represents a comments in /etc/hosts file.
127.0.0.1 localhost
192.217.44.207 nalanda metro
153.110.31.18 netserve
153.110.31.19 mainserver centeral
153.110.31.20 samsonite
64.202.167.10 ns3.secureserver.net
64.202.167.97 ns4.secureserver.net
66.249.89.104 www.google.com
68.178.157.132 services.amrood.com
请注意,一个给定的IP地址可能与多个名称相关联。从IP地址转换为主机名时使用该文件,反之亦然。
您将无权编辑此文件,因此,如果要将任何主机名和IP地址放在一起,则需要具有root权限。
5.客户端服务器模型
大多数的Net应用程序使用客户端-服务器体系结构,该体系结构是指两个进程或两个相互通信以交换某些信息的应用程序。这两个进程之一充当客户端进程,另一个进程充当服务器。
客户流程
这是过程,通常会要求提供信息。获得响应后,此过程可能会终止或可能会执行其他一些处理。
例如,Internet浏览器用作客户端应用程序,该应用程序向Web服务器发送请求以获取一个HTML网页。
服务器进程
这是从客户端接收请求的过程。从客户端收到请求后,此过程将执行所需的处理,收集请求的信息,然后将其发送给请求者客户端。完成后,就可以为其他客户提供服务了。服务器进程始终保持警惕,随时可以处理传入的请求。
示例 -Web服务器不断等待来自Internet浏览器的请求,并且一旦它从浏览器收到任何请求,它将获取一个请求的HTML页面并将其发送回该浏览器。
请注意,客户端需要知道服务器的地址,但是在建立连接之前,服务器不需要知道地址或什至客户端的存在。建立连接后,双方都可以发送和接收信息。
2层和3层架构
客户端-服务器架构有两种类型-
-
2层体系结构 -在这种体系结构中,客户端直接与服务器交互。这种类型的体系结构可能存在一些安全漏洞和性能问题。Internet Explorer和Web Server在两层体系结构上工作。使用安全套接字层(SSL)可以解决安全问题。
-
3层体系结构 -在这种体系结构中,客户端和服务器之间还有一个软件。该中间软件称为“中间件”。中间件用于在负载很重的情况下执行所有安全检查和负载平衡。中间件接收来自客户端的所有请求,并在执行所需的身份验证后,将该请求传递给服务器。然后,服务器执行所需的处理,并将响应发送回中间件,最后,中间件将此响应传递回客户端。如果要实现3层体系结构,则可以在Web服务器和Web浏览器之间保留任何中间件,例如Web Logic或WebSphere软件。
服务器类型
您可以拥有两种类型的服务器-
-
迭代服务器 -这是最简单的服务器形式,其中服务器进程为一个客户端提供服务,并且在完成第一个请求后,它将接收来自另一个客户端的请求。同时,另一个客户一直在等待。
-
并发服务器 -此类服务器运行多个并发进程以一次处理多个请求,因为一个进程可能花费更长的时间,而另一个客户端无法等待那么长时间。在Unix下编写并发服务器的最简单方法是派生一个子进程来分别处理每个客户端。
如何使客户
对于客户端和服务器,用于建立连接的系统调用有些不同,但是两者都涉及套接字的基本构造。这两个进程都建立自己的套接字。
在客户端建立套接字的步骤如下-
-
使用socket()系统调用创建一个套接字。
-
使用connect()系统调用将套接字连接到服务器的地址。
-
发送和接收数据。有很多方法可以做到这一点,但是最简单的方法是使用read()和write()系统调用。
如何制作服务器
在服务器端建立套接字的步骤如下-
-
使用socket()系统调用创建一个套接字。
-
使用bind()系统调用将套接字绑定到一个地址。对于Internet上的服务器套接字,地址由主机上的端口号组成。
-
监听与listen()系统调用的连接。
-
接受与accept()系统调用的连接。该调用通常会阻塞连接,直到客户端与服务器连接为止。
-
使用read()和write()系统调用发送和接收数据。
客户端和服务器交互
下图显示了客户端和服务器的完整交互-
6.结构
Unix套接字编程中使用了各种结构来保存有关地址和端口的信息以及其他信息。大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。本章定义的结构与Internet协议族有关。
sockaddr
第一个结构是sockaddr,它保存套接字信息-
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
这是一个通用的套接字地址结构,它将在大多数套接字函数调用中传递。下表提供了成员字段的描述-
属性 | 价值观 | 描述 |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK | 它代表一个地址族。在大多数基于Internet的应用程序中,我们使用AF_INET。 |
sa_data | 协议专用地址 | 协议特定地址的14个字节的内容根据地址的类型进行解释。对于Internet系列,我们将使用端口号IP地址,该地址由下面定义的sockaddr_in结构表示。 |
在sockaddr
帮助您引用套接字元素的第二种结构如下-
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
这是成员字段的描述-
属性 | 价值观 | 描述 |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK | 它代表一个地址族。在大多数基于Internet的应用程序中,我们使用AF_INET。 |
sin_port | 服务端口 | 网络字节顺序的16位端口号。 |
sin_addr | IP地址 | 网络字节顺序的32位IP地址。 |
sin_zero | 未使用 | 您只需将此值设置为NULL即可,因为未使用该值。 |
在地址
该结构仅在以上结构中用作结构字段,并拥有32位netid / hostid。
struct in_addr {
unsigned long s_addr;
};
这是成员字段的描述-
属性 | 价值观 | 描述 |
---|---|---|
s_addr | 服务端口 | 网络字节顺序的32位IP地址。 |
主人
此结构用于保留与主机有关的信息。
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list
#define h_addr h_addr_list[0]
};
这是成员字段的描述-
属性 | 价值观 | 描述 |
---|---|---|
h_name | ti.com等 | 它是主机的正式名称。例如,tutorialspoint.com,google.com等。 |
h_aliases | TI | 它包含主机名别名的列表。 |
h_addrtype | AF_INET | 它包含地址族,对于基于Internet的应用程序,它将始终为AF_INET。 |
h_length | 4 | 它保存IP地址的长度,对于Internet地址为4。 |
h_addr_list | in_addr | 对于Internet地址,指针数组h_addr_list [0],h_addr_list [1]等指向结构in_addr。 |
注 – h_addr定义为h_addr_list [0],以保持向后兼容性。
仆人
此特定结构用于保留与服务和相关端口有关的信息。
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
这是成员字段的描述-
属性 | 价值观 | 描述 |
---|---|---|
s_name | http | 这是服务的正式名称。例如,SMTP,FTP POP3等。 |
s_aliases | 别名 | 它包含服务别名列表。在大多数情况下,它将被设置为NULL。 |
运动 | 80 | 它将具有关联的端口号。例如,对于HTTP,该值为80。 |
s_proto | TCP协议 UDP协议 | 设置为使用的协议。Internet服务是使用TCP或UDP提供的。 |
套接字结构技巧
套接字地址结构是每个网络程序的组成部分。我们分配它们,填写它们,并将指向它们的指针传递给各种套接字函数。有时,我们将指向这些结构之一的指针传递给套接字函数,并填充其中的内容。
我们总是通过引用传递这些结构(即,我们传递指向结构的指针,而不是结构本身),并且我们总是传递结构的大小作为另一个参数。
当套接字函数填充结构时,该长度也通过引用传递,因此该函数可以更新其值。我们称这些值为结果的参数。
始终通过对bzero()函数使用memset()将结构变量设置为NULL(即'\ 0'),否则它可能会在结构中获得意外的垃圾值。