第十三夜:网络编程1-网络协议及其数据结构

没有人逼你学C语言,但它在复杂的计算世界中,曲径通幽。

socket相关的数据结构

sockaddr_in
sockaddr_in结构体定义在/usr/include/netinet/in.h中,它的定义是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 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)];
};

这个结构体中又涉及到另外几个类型和宏定义,包括__SOCKADDR_COMMON、in_port_t、in_addr。

__SOCKADDR_COMMON

__SOCKADDR_COMMON定义在bits/sockaddr.h当中,它的作用是将sin_和family参数拼接为对应的sa_family_t类型的地址家族。

1
2
3
4
5
6
7
8
9
/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;

/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc. */

#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family

也就是说这个预处理宏定义的意思是将sin_和family拼接在一起构成sa_family_t类型的sin_family。

sa_family_t就是短整形,长度16 bits的整数。

所以sockaddr_in结构体的第一个元素其实是sa_family_t类型的sin_family参数。

in_port_t

in_port_t实际上是uint16_t,16位的无符号整数。

1
typedef uint16_t in_port_t;

uint16_t其实就是__uint16_t,表示unsigned short int,它的长度是2个字节,能表示的范围是0-65535。

in_addr

这个显而易见,就是表示一个IPv4的地址:

1
2
3
4
5
6
/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};

而uint32_t也就是__uint32_t,表示unsigned int,一共4个字节。IP地址一般是拆成点分十进制看的,一个字节能表示的范围是0-255,所以的in_addr也就是能表示成0.0.0.0 ~ 255.255.255.255。

sin_zero

sin_zero的相关定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Structure describing an Internet (IP) socket address. */
#if __UAPI_DEF_SOCKADDR_IN
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */

/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero __pad /* for BSD UNIX comp. -FvK */
#endif

__pad声明了一个长素为8个字节的字符数组。

1
2
__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)
= 16 - 2 - 2 - 4 = 8

sin_family的类型

在C语言实现网络通信中,最开始需要做的就是声明并且初始化sockaddr_in结构体。
其中sin_family的类型有这些:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/* Protocol families.  */
#define PF_UNSPEC 0 /* Unspecified. */
#define PF_LOCAL 1 /* Local to host (pipes and file-domain). */
#define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */
#define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */
#define PF_INET 2 /* IP protocol family. */
#define PF_AX25 3 /* Amateur Radio AX.25. */
#define PF_IPX 4 /* Novell Internet Protocol. */
#define PF_APPLETALK 5 /* Appletalk DDP. */
#define PF_NETROM 6 /* Amateur radio NetROM. */
#define PF_BRIDGE 7 /* Multiprotocol bridge. */
#define PF_ATMPVC 8 /* ATM PVCs. */
#define PF_X25 9 /* Reserved for X.25 project. */
#define PF_INET6 10 /* IP version 6. */
#define PF_ROSE 11 /* Amateur Radio X.25 PLP. */
#define PF_DECnet 12 /* Reserved for DECnet project. */
#define PF_NETBEUI 13 /* Reserved for 802.2LLC project. */
#define PF_SECURITY 14 /* Security callback pseudo AF. */
#define PF_KEY 15 /* PF_KEY key management API. */
#define PF_NETLINK 16
#define PF_ROUTE PF_NETLINK /* Alias to emulate 4.4BSD. */
#define PF_PACKET 17 /* Packet family. */
#define PF_ASH 18 /* Ash. */
#define PF_ECONET 19 /* Acorn Econet. */
#define PF_ATMSVC 20 /* ATM SVCs. */
#define PF_RDS 21 /* RDS sockets. */
#define PF_SNA 22 /* Linux SNA Project */
#define PF_IRDA 23 /* IRDA sockets. */
#define PF_PPPOX 24 /* PPPoX sockets. */
#define PF_WANPIPE 25 /* Wanpipe API sockets. */
#define PF_LLC 26 /* Linux LLC. */
#define PF_IB 27 /* Native InfiniBand address. */
#define PF_MPLS 28 /* MPLS. */
#define PF_CAN 29 /* Controller Area Network. */
#define PF_TIPC 30 /* TIPC sockets. */
#define PF_BLUETOOTH 31 /* Bluetooth sockets. */
#define PF_IUCV 32 /* IUCV sockets. */
#define PF_RXRPC 33 /* RxRPC sockets. */
#define PF_ISDN 34 /* mISDN sockets. */
#define PF_PHONET 35 /* Phonet sockets. */
#define PF_IEEE802154 36 /* IEEE 802.15.4 sockets. */
#define PF_CAIF 37 /* CAIF sockets. */
#define PF_ALG 38 /* Algorithm sockets. */
#define PF_NFC 39 /* NFC sockets. */
#define PF_VSOCK 40 /* vSockets. */
#define PF_KCM 41 /* Kernel Connection Multiplexor. */
#define PF_QIPCRTR 42 /* Qualcomm IPC Router. */
#define PF_SMC 43 /* SMC sockets. */
#define PF_MAX 44 /* For now.. */


/* Address families. */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
#define AF_ROSE PF_ROSE
#define AF_DECnet PF_DECnet
#define AF_NETBEUI PF_NETBEUI
#define AF_SECURITY PF_SECURITY
#define AF_KEY PF_KEY
#define AF_NETLINK PF_NETLINK
#define AF_ROUTE PF_ROUTE
#define AF_PACKET PF_PACKET
#define AF_ASH PF_ASH
#define AF_ECONET PF_ECONET
#define AF_ATMSVC PF_ATMSVC
#define AF_RDS PF_RDS
#define AF_SNA PF_SNA
#define AF_IRDA PF_IRDA
#define AF_PPPOX PF_PPPOX
#define AF_WANPIPE PF_WANPIPE
#define AF_LLC PF_LLC
#define AF_IB PF_IB
#define AF_MPLS PF_MPLS
#define AF_CAN PF_CAN
#define AF_TIPC PF_TIPC
#define AF_BLUETOOTH PF_BLUETOOTH
#define AF_IUCV PF_IUCV
#define AF_RXRPC PF_RXRPC
#define AF_ISDN PF_ISDN
#define AF_PHONET PF_PHONET
#define AF_IEEE802154 PF_IEEE802154
#define AF_CAIF PF_CAIF
#define AF_ALG PF_ALG
#define AF_NFC PF_NFC
#define AF_VSOCK PF_VSOCK
#define AF_KCM PF_KCM
#define AF_QIPCRTR PF_QIPCRTR
#define AF_SMC PF_SMC
#define AF_MAX PF_MAX

其中最常用的AF_INET就是PF_INET,也就2所代表的IP协议家族。

sin_addr的赋值
sin_addr需要一个IPv4的地址作为值,in.h中定义了这样几个常用值,其中包括本地地址、多拨地址等:

写服务端socket最常用的INADDR_ANY地址就是((unsigned long int) 0x00000000),即 0.0.0.0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

/* Address to accept any incoming messages. */
#define INADDR_ANY ((unsigned long int) 0x00000000)

/* Address to send to all hosts. */
#define INADDR_BROADCAST ((unsigned long int) 0xffffffff)

/* Address indicating an error return. */
#define INADDR_NONE ((unsigned long int) 0xffffffff)

/* Network number for local host loopback. */
#define IN_LOOPBACKNET 127

/* Address to loopback in software to local host. */
#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)

/* Defines for Multicast INADDR */
#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */
#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */
#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */
#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */
#endif

socket的类型
关于socket的类型,在bits/socket_type.h中定义:

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
33
/* Types of sockets.  */
enum __socket_type
{
SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
byte streams. */
#define SOCK_STREAM SOCK_STREAM
SOCK_DGRAM = 2, /* Connectionless, unreliable datagrams
of fixed maximum length. */
#define SOCK_DGRAM SOCK_DGRAM
SOCK_RAW = 3, /* Raw protocol interface. */
#define SOCK_RAW SOCK_RAW
SOCK_RDM = 4, /* Reliably-delivered messages. */
#define SOCK_RDM SOCK_RDM
SOCK_SEQPACKET = 5, /* Sequenced, reliable, connection-based,
datagrams of fixed maximum length. */
#define SOCK_SEQPACKET SOCK_SEQPACKET
SOCK_DCCP = 6, /* Datagram Congestion Control Protocol. */
#define SOCK_DCCP SOCK_DCCP
SOCK_PACKET = 10, /* Linux specific way of getting packets
at the dev level. For writing rarp and
other similar things on the user level. */
#define SOCK_PACKET SOCK_PACKET

/* Flags to be ORed into the type parameter of socket and socketpair and
used for the flags parameter of paccept. */

SOCK_CLOEXEC = 02000000, /* Atomically set close-on-exec flag for the
new descriptor(s). */
#define SOCK_CLOEXEC SOCK_CLOEXEC
SOCK_NONBLOCK = 00004000 /* Atomically mark descriptor(s) as
non-blocking. */
#define SOCK_NONBLOCK SOCK_NONBLOCK
};

socket相关的函数

主要是定义在sys/socket.h中的以下函数:

  • socket
  • bind
  • listen
  • accept
  • connect

关于这些函数的介绍暂不赘述,sys/socket.h里写的很详细。这里用一个socket基础的例子来演示其用法:

服务端server.c:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
* 源码出处:https://www.cnblogs.com/uestc-mm/p/7630145.html
* 编译:gcc -m32 -o server server.c
*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[]){
int fd, new_fd, struct_len, numbytes;
struct sockaddr_in server_addr; // 定义在netinet/in.h
struct sockaddr_in client_addr;
char buff[BUFSIZ];//BUFSIZ定义在stdio.h,默认8192

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8000);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 8);
struct_len = sizeof(struct sockaddr_in);

fd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个TCP类型的socket

while(bind(fd, (struct sockaddr *)&server_addr, struct_len) == -1);
printf("Bind Success!\n");

while(listen(fd, 10) == -1);
printf("Listening(%d)....\n", fd);

printf("Ready for Accept...\n");

while( new_fd = accept(fd, (struct sockaddr *)&client_addr, &struct_len) ){
printf("Get the New Client: %d\n", new_fd);

numbytes = send(new_fd,"Welcome to my server\n",21,0);

while((numbytes = recv(new_fd, buff, BUFSIZ, 0)) > 0)
{
buff[numbytes] = '\0';
printf("%s\n",buff);
if(send(new_fd,buff,numbytes,0)<0)
{
perror("write");
return 1;
}
}
close(new_fd);
}

close(fd);
return 0;
}

客户端client.c

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
33
34
35
36
37
38
39
40
41
42
/*
* 源码出处:https://www.cnblogs.com/uestc-mm/p/7630145.html
* 编译:gcc -m32 -o client client.c
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc,char *argv[])
{
int sockfd,numbytes;
char buf[BUFSIZ];
struct sockaddr_in their_addr;
printf("break!");
while((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1);
printf("We get the sockfd~\n");
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(8000);
their_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
bzero(&(their_addr.sin_zero), 8);

while(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)) == -1);
printf("Get the Server~Cheers!\n");
numbytes = recv(sockfd, buf, BUFSIZ,0);//接收服务器端信息
buf[numbytes]='\0';
printf("%s",buf);
while(1)
{
printf("Entersome thing:");
scanf("%s",buf);
numbytes = send(sockfd, buf, strlen(buf), 0);
numbytes=recv(sockfd,buf,BUFSIZ,0);
buf[numbytes]='\0';
printf("received:%s\n",buf);
}
close(sockfd);
return 0;
}

参考资料

Linux下C语言的socket网络编程
socketpair的用法和理解
进程间通信:管道和socketpair的区别

第十四夜:网络编程2-Socket通信 第十二夜:使用C语言探索堆栈
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×