前言
在上一篇文章里面我们介绍了TCP的三次握手和四次挥手过程的介绍以及网络编程里面的一些api接口函数的介绍——Linux系统下socket编程之socket接口介绍(一)。今天我们继续来介绍网络编程里面的其它接口函数,为实战打下 基础;网络编程专题文章拖的有点久,这两天全部把它写完,不能再拖了。
函数介绍
- 发送和接收 -
(1)send和write:
首先说明的一点,之前介绍的socket这个函数,非常类似我们之前介绍的open函数,他们都会返回一下文件描述符;所以这里的send函数和write函数作用类似,我们用man手册来查看它的具体形式和用法:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
说明:
第一个参数sockfd就是socket函数返回的文件描述符;第二个参数指向发送的信息所在的缓冲区(内存);第三个参数指缓冲区的长度大小;第四个参数一般设置为0(如果不是这种情况,可以具体再查看)。不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
(2)recv和read:
函数recv和之前介绍的read函数的用法差不多,我们还是用man手册来查看一它的形式和用法:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
说明:
这里面的参数和上面send的参数说明一样。不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。
- IP地址格式转换函数 -
(1)inet_aton、inet_addr、inet_ntoa(只用IPV4的IP地址),现在用的比较少,不过大多程序里面会看到这些函数,所以还是要学习一下它的作用,老方法使用man手册来查看它的形式和用法:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
说明:
首先我们来看inet_aton函数,它转换网络主机地址ip(如192.168.1.10)为二进制数值,并存储在struct in_addr结构中,即第二个参数*inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。(这个转换完后不能用于网络传输,还需要调用htons或htonl函数才能将主机字节顺序转化为网络字节顺序,这两个函数先不讲,实战遇到的话,再进行解析),具体可以看下面Linux的源文:
inet_aton() converts the Internet
host address cp from the IPv4
numbers-and-dots notation into binary
form (in network byte order) and stores it in the
structure that inp points to.
inet_aton() returns nonzero if the address is valid,
zero if not. The address
supplied in cp can have one of the following forms:
a.b.c.d Each of the four numeric parts specifies a
byte of the address; the
bytes are assigned in left-to-right order to produce the binary address.
a.b.c Parts a and b specify the first two bytes of the binary address. Part c is interpreted as a 16-bit value that defines the rightmost two bytes of the binary address. This notation
is suitable for specifying (outmoded) Class B network addresses.
a.b Part a specifies the first byte of the binary address. Part b is
interpreted as a 24-bit value that defines the
rightmost three bytes of the binary address. This notation is suit‐
able for specifying (outmoded) Class A network addresses.
a The value a is interpreted as a 32-bit value that is stored
directly into the binary address without any byte rearrangement.
In all of the above forms, components of the dotted address can be specified
in decimal, octal (with a leading 0), or hexadecimal, with a leading 0X).
Addresses in any of these forms are
collectively termed IPV4 numbers-and-dots notation. The form that uses exactly
four decimal numbers is referred to as IPv4 dotted-decimal notation (or sometimes: IPv4 dotted-quad notation).
inet_aton() returns 1 if the supplied string was successfully interpreted,
or 0 if the string is invalid (errno is not set on error).
接着是inet_addr函数,它的作用主要是转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp无效,函数回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回-1,255.255.255.255是一个有效的地址,不过inet_addr无法处理:
The inet_addr() function converts the Internet
host address cp from IPv4 numbers-and-dots notation into
binary data in network byte order.
If the input is invalid, INADDR_NONE (usually -1)
is returned.
Use of this function is problematic because -1 is a
valid address (255.255.255.255).
Avoid its use in favor of inet_aton(), inet_pton(3),
or getaddrinfo(3), which provide a
cleaner way to indicate error return.
最后就是inet_ntoa函数,它的作用主要是转换网络字节排序的地址为标准的ASCII以点分开的地址,该函数返回指向点分开的字符串地址(如192.168.1.10)的指针,该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖),所以如果需要保存该串最后复制出来自己管理!
The inet_ntoa() function converts the Internet host
address in, given in network byte order, to a string in
IPv4 dotted-decimal notation. The string is returned
in a statically allocated
buffer, which subsequent calls will overwrite.
(2)inet_ntop、inet_pton,这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。我们还是用man手册来查看的它们的形式和作用:
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
我们先来看inet_ntop这个函数,第一个参数表示地址族(就是ipv4和ipv6),它的作用是把二进制格式转化为点分十进制的ip地址格式;inet_ntop函数的dst参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。size参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果size太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC:
This function converts the network address
structure src in the af address family into a
character string. The resulting string is copied to
the buffer pointed to by dst, which must be a
non-null pointer. The caller specifies the number of
bytes available in this buffer in the argument size.
inet_ntop() extends the inet_ntoa(3) function to
support multiple address families, inet_ntoa(3) is
now considered to be deprecated in favor of
inet_ntop(). The following address families
are currently supported:
AF_INET
src points to a struct in_addr (in network
byte order) which is converted to an IPv4 network
address in the dotted-decimal format,
"ddd.ddd.ddd.ddd". The buffer dst must be at least
INET_ADDRSTRLEN bytes long.
AF_INET6
src points to a struct in6_addr (in network
byte order) which is converted to a representation of
this address in the most appropriate IPv6 network
address format for this address.
The buffer dst must be at least
INET6_ADDRSTRLEN bytes long.
RETURN VALUE
On success, inet_ntop() returns a non-null pointer to
dst. NULL is returned if there was an error, with
errno set to indicate the error.
ERRORS
EAFNOSUPPORT
af was not a valid address family.
ENOSPC The converted address string would exceed the
size given by size.
接着我们来看inet_pton函数,它的作用主要是将点分十进制的ip地址转化为二进制格式:
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
DESCRIPTION
This function converts the character string src
into a network address structure in the af address
family, then copies the network address structure to
dst. The af argument must be either
AF_INET or AF_INET6. dst is written in network byte
order.
The following address families are currently
supported:
AF_INET
src points to a character string containing an
IPv4 network address in dotted-decimal format,
"ddd.ddd.ddd.ddd", where ddd is a decimal number of up
to three digits in the range 0 to
255. The address is converted to a struct
in_addr and copied to dst, which must be sizeof(struct
in_addr) (4) bytes (32 bits) long.
AF_INET6
src points to a character string containing
an IPv6 network address. The address is converted to
a struct in6_addr and copied to dst, which must be
sizeof(struct in6_addr) (16) bytes
(128 bits) long. The allowed formats for IPv6
addresses follow these rules:
1. The preferred format is x:x:x:x:x:x:x:x.
This form consists of eight hexadecimal numbers, each
of which expresses a 16-bit value (i.e., each x can
be up to 4 hex digits).
2. A series of contiguous zero values in the
preferred format can be abbreviated to ::. Only one
instance of :: can occur in an address. For
example, the loopback address
0:0:0:0:0:0:0:1 can be abbreviated as ::1.
The wildcard address, consisting of all zeros, can be
written as ::.
3. An alternate format is useful for
expressing IPv4-mapped IPv6 addresses. This form is
written as x:x:x:x:x:x:d.d.d.d, where the six leading
xs are hexadecimal values that define
the six most-significant 16-bit pieces of
the address (i.e., 96 bits), and the ds express a value
in dotted-decimal notation that defines the least
significant 32 bits of the
address. An example of such an address is
::FFFF:204.152.189.116.
See RFC 2373 for further details on the
representation of IPv6 addresses.
RETURN VALUE
inet_pton() returns 1 on success (network address
was successfully converted). 0 is returned if src does
not contain a character string representing a valid
network address in the specified
address family. If af does not contain a valid
address family, -1 is returned and errno is set to
EAFNOSUPPORT.
- 表示IP地址相关数据结构 -
(1)上面的一些函数参数里面用到的结构体(比如bind函数参数里的const struct sockaddr *addr等)都定义在 netinet/in.,我们可以用 vim /usr/include/netinet/in.h 来查看,这里具体的我就不理出来了,里面的内容比较多。
(2)struct sockaddr,这个结构体是linux的网络编程接口中用来表示IP地址的
标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in或者一个struct sockaddr_in6所填充。
(3)typedef uint32_t in_addr_t:网络内部用来表示IP地址的类型。
(4)struct in_addr
{
in_addr_t s_addr;
};
(5)struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
总结
今天主要是介绍了一下函数用法,下一篇文章开始进入实战讲解!
关注公众号,每周分享至少3篇开源技术干货,文章中如有没看懂的地方可以私聊我,