编译执行
代码语言:javascript复制emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc tcpclient.x tcpclient.c ; gtc tcpserver.x tcpserver.c
emacs@ubuntu:~/c$
此时系统中并没有开放9000端口
代码语言:javascript复制emacs@ubuntu:~/c$ netstat -ant | grep 9000
emacs@ubuntu:~/c$
运行服务端
代码语言:javascript复制emacs@ubuntu:~/c$ ./tcpserver.x
Listening...
此时系统中多了一个9000端口
代码语言:javascript复制emacs@ubuntu:~/c$ netstat -ant | grep 9000
tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN
emacs@ubuntu:~/c$
运行客户端,会立刻返回
代码语言:javascript复制emacs@ubuntu:~/c$ ./tcpclient.x 127.0.0.1 hello
2 -->OK
emacs@ubuntu:~/c$
服务端会打印信息并且返回
代码语言:javascript复制emacs@ubuntu:~/c$ ./tcpserver.x
Listening...
Received a message:hello
emacs@ubuntu:~/c$
编译执行过程中没有报错,从结果来看,符合预期
sockaddr 结构体
bits/socket.h
中有关于 sockaddr 结构体的定义
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
in_addr 结构体
netinet/in.h
中有关于 in_addr 结构体的定义
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
sockaddr_in 结构体
netinet/in.h
中有关于 sockaddr_in 结构体的定义
/* Structure describing an Internet socket address. */
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)];
};
从 sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)]
中可以看出
这个字段,是为填补与 sockaddr
结构体的长度差
二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别
socket
sys/socket.h
中有关于 socket 函数的声明
/* Create a new socket of type TYPE in domain DOMAIN, using
protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
Returns a file descriptor for the new socket, or -1 for errors. */
extern int socket (int __domain, int __type, int __protocol) __THROW;
用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作
__domain
即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址
__type
指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
__protocol
指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议
Note: 并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议
AF_INET 和 SOCK_STREAM 宏定义
bits/socket.h
中有关于 AF_INET 和 SOCK_STREAM 的宏定义
emacs@ubuntu:/usr/include$ grep AF_INET bits/socket.h
#define AF_INET PF_INET
#define AF_INET6 PF_INET6
emacs@ubuntu:/usr/include$ grep PF_INET bits/socket.h
#define PF_INET 2 /* IP protocol family. */
#define PF_INET6 10 /* IP version 6. */
#define AF_INET PF_INET
#define AF_INET6 PF_INET6
emacs@ubuntu:/usr/include$ grep SOCK_STREAM bits/socket.h
SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
#define SOCK_STREAM SOCK_STREAM
emacs@ubuntu:/usr/include$
使用此方法可以获取其它想要的宏定义
htons
netinet/in.h
中有关于 htons 的定义
/* Functions to convert between host and network byte order.
Please note that these functions normally take `unsigned long int' or
`unsigned short int' values as arguments and also return them. But
this was a short-sighted decision since on different systems the types
may have different representations but the values are always the same. */
extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
__THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
__THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
__THROW __attribute__ ((__const__));
网络字节顺序与系统字节顺序不一定相同
网络字节顺序(大端顺序)是指一个数在内存中存储的时候“高对低,低对高”(即一个数的高位字节存放于低地址单元,低位字节存放在高地址单元中)。但是计算机的内存存储数据时有可能是大端顺序或者小端顺序
而上面的函数就是用来进行这方面转化工作的
- h:host 本地主机端
- to:就是to,转化为
- n:net 网络端
- l:是 unsigned long (32bit)
- s:是 unsigned short (16bit)
ntohl
无符号长整型,从网络到本机
ntohs
无符号短整型,从网络到本机
htonl
无符号长整型,从本机到网络
htons
无符号短整型,从本机到网络
INADDR_ANY 宏定义
netinet/in.h
中有关于 INADDR_ANY 的定义
/* Address to accept any incoming messages. */
#define INADDR_ANY ((in_addr_t) 0x00000000)
/* Address to send to all hosts. */
#define INADDR_BROADCAST ((in_addr_t) 0xffffffff)
/* Address indicating an error return. */
#define INADDR_NONE ((in_addr_t) 0xffffffff)
INADDR_ANY 代表通配 0.0.0.0
setsockopt
sys/socket.h
中有关于 setsockopt 的定义
/* Set socket FD's option OPTNAME at protocol level LEVEL
to *OPTVAL (which is OPTLEN bytes long).
Returns 0 on success, -1 for errors. */
extern int setsockopt (int __fd, int __level, int __optname,
__const void *__optval, socklen_t __optlen) __THROW;
__fd
指向一个打开的套接口描述字
__level
指定选项代码的类型
__optname
选项名称
__optval
是一个指向变量的指针,类型为整形
__optlen
optval 的size大小
标志打开或关闭某个特征的二进制选项
closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket
代码语言:javascript复制BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
bind
sys/socket.h
中有关于 bind 的定义
/* Give the socket FD the local address ADDR (which is LEN bytes long). */
extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
__THROW;