目录
int inet_aton(const char * strptr,struct in_addr * addrptr)
in_addr_t inet_addr(const char * strptr)
字符* inet_ntoa(struct in_addr inaddr)
7.端口与服务
当客户端进程要连接服务器时,客户端必须有一种方法来标识要连接的服务器。如果客户端知道服务器所在主机的32位Internet地址,则可以联系该主机。但是客户端如何识别在该主机上运行的特定服务器进程?
为了解决标识主机上运行的特定服务器进程的问题,TCP和UDP都定义了一组知名端口。
就我们的目的而言,端口将定义为1024到65535之间的整数。这是因为所有小于1024的端口号都被认为是众所周知的 -例如,telnet使用端口23,http使用80,ftp使用21,等等。
网络服务的端口分配可以在文件/ etc / services中找到。如果要编写自己的服务器,则必须注意为服务器分配端口。您应确保不要将此端口分配给任何其他服务器。
通常,分配任何大于5000的端口号是一种惯例。但是,有许多组织编写的服务器的端口号大于5000的组织。例如,Yahoo Messenger在5050上运行,SIP Server在5060上运行,等等。
示例端口和服务
这是服务和相关端口的一小部分。您可以在IANA-TCP / IP端口分配中找到Internet端口和相关服务的最新列表。
服务 | 端口号 | 服务说明 |
回声 | 7 | UDP / TCP发回它收到的内容。 |
丢弃 | 9 | UDP / TCP丢弃输入。 |
白天 | 13 | UDP / TCP返回ASCII时间。 |
充电 | 19 | UDP / TCP返回字符。 |
ftp | 21 | TCP文件传输。 |
远程登录 | 23 | TCP远程登录。 |
短信 | 25 | TCP电子邮件。 |
白天 | 37 | UDP / TCP返回二进制时间。 |
ftp | 69 | UDP普通文件传输。 |
手指 | 79 | 用户的TCP信息。 |
http | 80 | TCP万维网。 |
登录 | 513 | TCP远程登录。 |
谁 | 513 | UDP有关用户的其他信息。 |
X服务器 | 6000 | TCP X窗口(NB> 1023)。 |
端口和服务功能
Unix提供以下功能来从/ etc / services文件中获取服务名称。
-
struct servent * getservbyname(char * name,char * proto) -此调用采用服务名称和协议名称,并返回该服务的相应端口号。
-
struct servent * getservbyport(int port,char * proto) -此调用获取端口号和协议名称,并返回相应的服务名称。
每个函数的返回值是指向具有以下形式的结构的指针:
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提供的。
|
8.网络字节序
不幸的是,并非所有计算机都以相同的顺序存储包含多字节值的字节。考虑一个由2个字节组成的16位Internet。有两种存储此值的方法。
-
小尾数 -在此方案中,低位字节存储在起始地址(A)上,高位字节存储在下一个地址(A +1)上。
-
大字节序-在此方案中,高位字节存储在起始地址(A)上,低位字节存储在下一个地址(A +1)上。
为了允许具有不同字节顺序约定的机器相互通信,Internet协议为通过网络传输的数据指定了规范的字节顺序约定。这称为网络字节顺序。
建立Internet套接字连接时,必须确保sockaddr_in结构的sin_port和sin_addr成员中的数据以网络字节顺序表示。
字节排序功能
在主机的内部表示和网络字节顺序之间转换数据的例程如下-
功能 | 描述 |
---|---|
htons() | 主机到网络短路 |
htonl() | 主机到网络长 |
ntohl() | 网络主机长 |
ntohs() | 主机网络短 |
下面列出的是有关这些功能的更多详细信息-
-
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(unsigned long netlong) -此函数将32位数量从网络字节顺序转换为主机字节顺序。
这些函数是宏,导致将转换源代码插入到调用程序中。在little-endian计算机上,代码会将值更改为网络字节顺序。在big-endian机器上,由于不需要插入任何代码,因此不会插入任何代码。这些函数定义为null。
确定主机字节顺序的程序
将以下代码保存在byteorder.c文件中,然后对其进行编译并在您的计算机上运行。
在此示例中,我们将两个字节的值0x0102存储在短整数中,然后查看两个连续的字节c [0](地址A)和c [1](地址A +1)以确定该字节订购。
#include <stdio.h>
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
}un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
}
else {
printf("sizeof(short) = %d\n", sizeof(short));
}
exit(0);
}
该程序在机器上生成的输出如下-
$> gcc byteorder.c
$> ./a.out
little-endian
$>
9.IP地址功能
Unix提供了各种函数调用来帮助您操纵IP地址。这些函数在ASCII字符串(人们喜欢使用的字符串)和网络字节排序的二进制值(存储在套接字地址结构中的值)之间转换Internet地址。
以下三个函数调用用于IPv4寻址-
- int inet_aton(const char * strptr,struct in_addr * addrptr)
- in_addr_t inet_addr(const char * strptr)
- 字符* inet_ntoa(struct in_addr inaddr)
int inet_aton(const char * strptr,struct in_addr * addrptr)
此函数调用将Internet标准点表示法中的指定字符串转换为网络地址,并将该地址存储在提供的结构中。转换后的地址将按网络字节顺序(字节从左到右顺序)。如果字符串有效,则返回1,错误则返回0。
以下是用法示例-
#include <arpa/inet.h>
(...)
int retval;
struct in_addr addrptr
memset(&addrptr, '\0', sizeof(addrptr));
retval = inet_aton("68.178.157.132", &addrptr);
(...)
in_addr_t inet_addr(const char * strptr)
此函数调用将Internet标准点表示法中的指定字符串转换为适合用作Internet地址的整数值。转换后的地址将按网络字节顺序(字节从左到右顺序)。它返回32位二进制网络字节排序的IPv4地址,并在出错时返回INADDR_NONE。
以下是用法示例-
#include <arpa/inet.h>
(...)
struct sockaddr_in dest;
memset(&dest, '\0', sizeof(dest));
dest.sin_addr.s_addr = inet_addr("68.178.157.132");
(...)
字符* inet_ntoa(struct in_addr inaddr)
此函数调用将指定的Internet主机地址转换为Internet标准点表示法中的字符串。
以下是用法示例-
#include <arpa/inet.h>
(...)
char *ip;
ip = inet_ntoa(dest.sin_addr);
printf("IP Address is: %s\n",ip);
(...)
10.核心功能
本章介绍编写完整的TCP客户端和服务器所需的核心套接字功能。
下图显示了客户端和服务器的完整交互-
套接字功能
要执行网络I / O,进程必须做的第一件事是调用套接字函数,指定所需的通信协议类型和协议族等
#include <sys/types.h>
#include <sys/socket.h>
int socket (int family, int type, int protocol);
该调用返回一个套接字描述符,您可以在以后的系统调用中使用它,或者在错误时使用-1。
参量
family-它指定协议家族,是下面显示的常量之一-
家庭 | 描述 |
---|---|
AF_INET | IPv4协议 |
AF_INET6 | IPv6协议 |
AF_LOCAL | Unix域协议 |
AF_ROUTE | 路由插座 |
AF_KEY | 插座 |
本章不涵盖IPv4以外的其他协议。
类型 -它指定所需的套接字类型。它可以采用以下值之一-
类型 | 描述 |
---|---|
SOCK_STREAM | 流插座 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 顺序数据包套接字 |
SOCK_RAW | 原始插座 |
protocol-参数应设置为以下给定的特定协议类型,或设置为0以选择给定的family和type组合的系统默认值-
协议 | 描述 |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
在连接功能
在连接功能使用TCP客户端建立与TCP服务器的连接。
<span style="color:rgba(0, 0, 0, 0.87)">#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);</span>
如果成功连接到服务器,则此调用返回0,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
serv_addr-它是指向结构sockaddr的指针,其中包含目标IP地址和端口。
-
addrlen-将其设置为sizeof(struct sockaddr)。
该绑定功能
所述绑定功能的本地协议地址分配给一个套接字。对于Internet协议,协议地址是32位IPv4地址或128位IPv6地址以及16位TCP或UDP端口号的组合。此函数仅由TCP服务器调用。
<span style="color:rgba(0, 0, 0, 0.87)">#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr,int addrlen);</span>
如果成功绑定到该地址,则此调用返回0,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
my_addr-它是指向sockaddr的指针,该指针包含本地IP地址和端口。
-
addrlen-将其设置为sizeof(struct sockaddr)。
您可以自动输入IP地址和端口
端口号值为0表示系统将选择一个随机端口,而IP地址的INADDR_ANY值表示将自动分配服务器的IP地址。
server.sin_port = 0;
server.sin_addr.s_addr = INADDR_ANY;
注 –保留所有低于1024的端口。除非其他程序正在使用该端口,否则您可以将端口设置为1024以上和65535以下。
监听功能
该监听功能只能通过TCP服务器调用,它会执行两个动作-
-
listen函数将未连接的套接字转换为被动套接字,指示内核应接受针对该套接字的传入连接请求。
-
此函数的第二个参数指定内核应为此套接字排队的最大连接数。
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd,int backlog);
该调用成功返回0,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
待办事项 -这是允许的连接数。
在接受功能
在接受功能由TCP服务器调用返回从已完成连接队列前面的下一个已完成连接。呼叫的签名如下-
#include <sys/types.h>
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
此调用在成功时返回非负描述符,否则在错误时返回-1。假定返回的描述符是客户端套接字描述符,并且所有读写操作都将在此描述符上进行以与客户端进行通信。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
cliaddr-它是指向结构sockaddr的指针,其中包含客户端IP地址和端口。
-
addrlen-将其设置为sizeof(struct sockaddr)。
该发送功能
该发送功能用于通过流插座或连接的数据报套接字发送数据。如果要通过UNCONNECTED数据报套接字发送数据,则必须使用sendto()函数。
您可以使用write()系统调用来发送数据。其签名如下-
<span style="color:rgba(0, 0, 0, 0.87)">int send(int sockfd, const void *msg, int len, int flags);
</span>
此调用返回发送的字节数,否则将在错误时返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
msg-它是您要发送的数据的指针。
-
len-这是您要发送的数据长度(以字节为单位)。
-
标志 -设置为0。
该recv的功能
所述的recv功能用于接收通过流插座或连接的数据报插座的数据。如果要通过UNCONNECTED数据报套接字接收数据,则必须使用recvfrom()。
您可以使用read()系统调用来读取数据。该调用在辅助函数章节中进行了说明。
int recv(int sockfd, void *buf, int len, unsigned int flags);
此调用返回读入缓冲区的字节数,否则将在错误时返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
buf-它是读取信息的缓冲区。
-
len-这是缓冲区的最大长度。
-
标志 -设置为0。
该SENDTO功能
该SENDTO功能用于在未连接的数据报套接字发送数据。其签名如下-
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
此调用返回发送的字节数,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
msg-它是您要发送的数据的指针。
-
len-这是您要发送的数据长度(以字节为单位)。
-
标志 -设置为0。
-
到 -它是指向要向其发送数据的主机的struct sockaddr的指针。
-
tolen-将其设置为sizeof(struct sockaddr)。
该recvfrom的功能
该recvfrom的功能是用来接收来自未连接的数据报套接字数据。
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
该调用返回读入缓冲区的字节数,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
buf-它是读取信息的缓冲区。
-
len-这是缓冲区的最大长度。
-
标志 -设置为0。
-
从 -它是一个指向结构sockaddr对于其中数据必须被读出的主机。
-
fromlen-将其设置为sizeof(struct sockaddr)。
该接近功能
在靠近函数用于关闭客户端和服务器之间的通信。它的语法如下-
int close( int sockfd );
该调用成功返回0,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
在关机功能
该关断功能用于正常关闭客户端和服务器之间的通信。与关闭功能相比,此功能可提供更多控制。下面给出的是shutdown的语法-
<span style="color:rgba(0, 0, 0, 0.87)">int shutdown(int sockfd, int how);
</span>
该调用成功返回0,否则返回-1。
参量
-
sockfd-这是套接字函数返回的套接字描述符。
-
如何 -输入数字之一-
-
0-表示不允许接收,
-
1-表示不允许发送,并且
-
2-表示不允许发送和接收。将how设置为2时,它与close()相同。
-
在选择功能
的选择功能指示哪些指定的文件描述符准备好读取,准备好写入,或具有待处理的错误条件。
当应用程序调用recv或recvfrom时,它将被阻止,直到数据到达该套接字为止。当传入数据流为空时,应用程序可能正在执行其他有用的处理。另一种情况是应用程序从多个套接字接收数据。
在其输入队列中没有数据的套接字上调用recv或recvfrom会阻止立即从其他套接字接收数据。select函数调用通过允许程序轮询所有套接字句柄以查看它们是否可用于非阻塞读取和写入操作来解决此问题。
下面给出的是select的语法-
<span style="color:rgba(0, 0, 0, 0.87)">int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
</span>
该调用成功返回0,否则返回-1。
参量
-
nfds-它指定要测试的文件描述符的范围。select()函数测试文件描述符的范围是0到nfds-1
-
readfds-它指向类型为fd_set的对象,该对象在输入时指定要检查的文件描述符,以准备好读取,而在输出时,指示要准备读取的文件描述符。可以为NULL表示空集。
-
writefds-指向类型为fd_set的对象,该对象在输入时指定要检查的文件描述符是否准备好写入,在输出时指示要准备写入的文件描述符。可以为NULL表示空集。
-
excludefds-指向类型为fd_set的对象,该对象在输入时指定要检查的文件描述符是否存在错误条件,在输出时指出哪些文件描述符具有错误条件未决。可以为NULL表示空集。
-
超时 -它指向的timeval结构指定多久select调用应查询描述符可用的I / O操作。如果超时值为0,则select将立即返回。如果timeout参数为NULL,则select将阻塞,直到至少一个文件/套接字句柄为可用的I / O操作准备就绪为止。否则,将在超时时间过后或至少有一个文件/套接字描述符为I / O操作准备就绪时返回select。
select的返回值是在文件描述符集中为I / O准备就绪的句柄数。如果达到了由超时字段指定的时间限制,请选择return0。存在以下用于处理文件描述符集的宏-
-
FD_CLR(fd,&fdset) -清除文件描述符集fdset中文件描述符fd的位。
-
FD_ISSET(fd,&fdset) -如果在fdset指向的文件描述符集中设置了文件描述符fd的位,则返回非零值,否则返回0。
-
FD_SET(fd,&fdset) -在文件描述符集fdset中设置文件描述符fd的位。
-
FD_ZERO(&fdset) -初始化文件描述符集fdset以使所有文件描述符的比特为零。
如果fd参数小于0或大于或等于FD_SETSIZE,则这些宏的行为是不确定的。
例
fd_set fds;
struct timeval tv;
/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;
/* tv now represents 1.5 seconds */
FD_ZERO(&fds);
/* adds sock to the file descriptor set */
FD_SET(sock, &fds);
/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(sock, &fds)) {
recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
/* do something */
}
else {
/* do something else */
}