C++-后端开发入门-TCP服务器编程

格式转换

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <arpa/inet.h>
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
in_addr_t inet_addr(const char* __cp); //将字符串点分十进制转为二进制地址 网络字节序 失败-1
char* inet_ntoa(struct in_addr __in); //将结构IP地址转为点分十进制字符串

//例如
in_addr_t dwIP=inet_addr("172.16.2.6");
struct in_addr ia;
ia.s_addr=dwIP;
printf("real_ip=%s\n",inet_ntoa(ia));

套接字常用结构

⼀个套接字代表通信的⼀端,每端都有⼀个套接字地址,包含了IP地址和端⼝信息。套接字地址分为通用套接字地址和专用套接字地址。

通用套接字地址结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Structure describing a generic socket address.  */
struct sockaddr {
__SOCKADDR_COMMON(sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
/*
sa_family取值如下:
PF_UNIX:UNIX本地域协议族
PF_INET:IPv4协议族
PF_INET6:IPv6协议族
AF_UNIX:UNIX本地域地址族
AF_INET:IPv4地址族
AF_INET6:IPv6地址族
*/

该结构过小,如IPv6和UNIX等地址长度超长,且为了内存对齐,Linux采用新的通用套接字地址结构:

1
2
3
4
5
6
#define __ss_aligntype    unsigned long int
struct sockaddr_storage {
__SOCKADDR_COMMON(ss_); /* Address family, etc. */
char __ss_padding[_SS_PADSIZE];
__ss_aligntype __ss_align; /* Force desired alignment. */
};

Linux为不同协议族定义了不同套接字地址结构体,称为专用地址结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//IPv4专用
/* Type to represent a port. */
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
struct in_addr{
in_addr_t s_addr;
};
/* 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. */
unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof(in_port_t) - sizeof(struct in_addr)]; /* Pad to size of `struct sockaddr'. */
};

//IPv6专用
/* IPv6 address */
struct in6_addr {
union {
uint8_t __u6_addr8[16];
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
} __in6_u;
};
/* Ditto, for IPv6. */
struct sockaddr_in6 {
__SOCKADDR_COMMON(sin6_);
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};

当一个套接字绑定了地址,可获取它的套接字地址。套接字通信需要在本地和远程两端建立套接字,所以获取套接字地址分为获取本地套接字地址和获取远程套接字地址。getsockname可在以下两种情况下获取本地套接字地址,并可用getpeername获取通信对端套接字地址。

  • 本地套接字用bind获取地址。
  • 本地套接字没有绑定地址,但用connect和远程建立了连接,此时内核分配一个地址给本地套接字。
1
2
3
4
5
6
7
8
9
10
11
#include <sys/socket.h>
int getsockname(
int sockfd, //套接字描述符
struct sockaddr* localaddr, //本地套接字地址
socklen_t* addrlen //本地套接字地址结构体大小 单位字节
); //成功0 出错-1
int getpeername(
int sockfd,
struct sockaddr* peeraddr,
socklen_t* addrlen
);

P243