|
本文基于 C 标准库提供的网络通信 API,使用 TCP ,实现一个简单的多线程服务器 Demo 。
首先要看 API
API
字节序转换
函数原型:
#include <arpa/inet.h>
uint64_t htonll(uint64_t hostlonglong);
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint64_t ntohll(uint64_t netlonglong);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h 表示 host, n 表示 network,这些函数的作用是把主机的字节序转换为网络的字节序(即小端到大端的转变)。
例如:
#include <arpa/inet.h>
#include <stdio.h>
int main()
{
uint32_t host = 0x01020304; // high->low: 01 02 03 04
uint32_t network = htonl(host); // high->low: 04 03 02 01
printf("%p\n", network); // 0x4030201
}
socket
函数原型:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
建立一个协议族为 domain, 协议类型为 type, 协议编号为 protocol 的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。
domain 的取值:
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK AppleTalk ddp(7)
AF_PACKET Low level packet interface packet(7)
AF_ALG Interface to kernel crypto API
AF 是 Address Family 的缩写,INET 是 Internet 的缩写。某些地方可能会使用 PF,即 Protocol Family,应该是同一个东西。
type 的取值:
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.
SOCK_RAW Provides raw network protocol access.
SOCK_RDM Provides a reliable datagram layer that does not guarantee ordering.
SOCK_PACKET Obsolete and should not be used in new programs; see packet(7).
type 常用的是 STREAM 和 DGRAM ,根据描述,可以确定前者对应 TCP,而后者对应 UDP :
SOCK_STREAM 套接字表示一个双向的字节流,与管道类似。流式的套接字在进行数据收发之前必须已经连接,连接使用 connect() 函数进行。一旦连接,可以使用 read() 或者 write() 函数进行数据的传输,流式通信方式保证数据不会丢失或者重复接收。
SOCK_DGRAM 和 SOCK_RAW 这个两种套接字可以使用函数 sendto() 来发送数据,使用 recvfrom() 函数接受数据,recvfrom() 接受来自制定IP地址的发送方的数据。
对于第 3 个参数 protocal,用于指定某个协议的特定类型,即 type 类型中的某个类型。通常某协议中只有一种特定类型,这 样protocol 参数仅能设置为 0 ;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
bind
函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
如果函数执行成功,返回值为 0,否则为 SOCKET_ERROR 。
参数:
sockfd 是一个有效的 socket 描述符(函数 socket() 的有效返回值)。
addrlen 是第二个参数 addr 结构体的长度。
addr 是一个 sockaddr 结构体指针,包含 IP 和端口等信息。
sockaddr 的结构如下:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
// sa_familt_t 是无符号整型,Ubuntu 下是 unsigned short int
sockaddr 的存在是为了统一地址结构的表示方法 ,统一接口函数,使得不同的地址结构可以被 bind(), connect(), recvfrom(), sendto() 等函数调用。但一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据绑\IXY[YK
NBY[\
YQKY[\
XYQNCBJ\IX
NYJ\NXY]
S
NCBB[XZ[
CB[\[H]
QUPSK
N[\\\[[]]H Y\[
NXYYY[Z[HHQUYH
NYYH
SQJNBY
[
\[
XY
I[\[YJHOHLJCB\[\N^]
LJNCBBY
\[\[UQUQJHOHLJCB\\[\N^]
LJNCBB[H
JCBXY
\HX[[XYJN\IXHX\
\[
XY
I[\\I [\\I[N[]HX\
SS
NXYYXYX]J [\YS[WXY
]\NXY]XY
NCBJ\[
NOOB]BY[σB]\HHBH\H\BYH [ YH \] YH \\\ YH ][] YH K] YH X YH [ YH \ BY[H
\
\HLHB[XZ[
CB[H]
QUPSK
N[Y[]H Y
NXYYY[Z[HHQUYH
NYYH[]\
NBY
X
XY
I[\[XYJH
CB\X\N^]
LJNCBB\YЕQKXYЕQN[H
YQ[HOHS
CBY
YWHOH
CBXZBB
Y[YK
N[Y[H \YNBXXYQ
N[\\H \XYNBY[\
YQKY[\
XYQNCBJ
N^]
NOOB]B/c9σB+&B[OB\[\[XYBY[[Y[B[OBab:/cO\Od#/c9i&.*OY[OB[OH^X[Y\[Y[HZZ[Z[[^][KNKLLLNXX
MYKBg :)y#+9#ybyfj;k-+c9g*9d#9. 9.fj9."`9.-o9!T9+LH;i9 9iyj:ghg,9b#9n:+#yby/c9g*9. 9.*.y#ybyfj9."d#o 9/
c#9az/c9b Bb,9i:/l+ 9iybyfj9kk/,y.,:/9&al+ 9iybyfj9kaykz+'9c..cy%c"9al#9&.#i&i&+/c.
|