0%

Linux C 网络编程

第一章

第二章

第三章

网络字节序与地址转换

保存数据方式 解析数据方式
大端序 高位字节放在低位地址
小端序 低位字节放在高位地址

字节序转换相关函数

​ short–>占用2字节、long–>占用4字节(Linux)

函数名 作用等
htonl() 用于IP地址转换
htons() 用于端口号转换
字节序转换函数实例
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
#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char **argv)
{
unsigned short host_port = 0x1234;
unsigned short net_port;
unsigned long host_addr = 0x12345678;
unsigned long net_addr;

net_addr = htonl(host_addr);
net_port = htons(host_port);

printf("Host order port: %#x\n", host_port);
printf("Net order port: %#x\n", net_port);
printf("Host order address: %#lx\n", host_addr);
printf("Net order address: %#lx\n", net_addr);

return 0;
}

/************* 执行结果 ************/
Host order port: 0x1234
Net order port: 0x3412
Host order address: 0x12345678
Net order address: 0x78563412

数据在传输过程中不需要考虑字节序问题(除了向sockaddr_in填充数据外)

网络地址的初始化与分配

将字符串信息转换为网络字节序的整数型

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
/************* 头文件等 *************/
#include <arpa/inet.h>

in_addr_t inet_addr(const char *string);
成功返回32位大端序整数型值,失败返回INADDR_NONE

/************* 代码示例 ************/
#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char **argv)
{
char *addr1 = "1.2.3.4";
char *addr2 = "1.2.3.256";

unsigned long conv_addr = inet_addr(addr1);
if (conv_addr == INADDR_NONE)
printf("Error occurred! \n");
else
printf("Network ordered integer addr: %#lx \n", conv_addr);

conv_addr = inet_addr(addr2);
if (conv_addr == INADDR_NONE)
printf("Error occurred! \n");
else
printf("Network ordered integer addr: %#lx \n", conv_addr);

return 0;
}

/************* 执行结果 ************/
Network ordered integer addr: 0x4030201
Error occurred!

一个字节能表示的最大整数位255,所以第二个错的
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
/************* 头文件等 *************/
#include <arpa/inet.h>

int inet_aton(const char * string, struct in_addr *addr);
成功返回1,失败返回0

string: 含有需要转换的IP地址信息的字符串地址值
addr: 将保存转换结果的in_addr结构体变量的地址值
/************* 代码示例 ************/
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>

void error_handler(char *message);

int main(int argc, char **argv)
{
char *addr = "127.232.124.79";
struct sockaddr_in addr_inet;

if (!inet_aton(addr, &addr_inet.sin_addr))
{
error_handler("Conversion error");
}
else
{
printf("NetWork order integer addr: %#x \n", addr_inet.sin_addr.s_addr);
}

return 0;
}

void error_handler(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

/************* 执行结果 ************/
NetWork order integer addr: 0x4f7ce87f

inet_addr函数和inet_aton函数功能完全相同。但是inet_aton函数会自动把结果存入sockaddr_in结构体中

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
/************* 头文件等 *************/
#include <arpa/inet.h>

char *inet_aton(struct in_addr addr);
成功返回转换字符串地址值,失败返回-1

/************* 代码示例 ************/
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
struct sockaddr_in addr1, addr2;
char *str_ptr;
char str_arr[20];

addr1.sin_addr.s_addr = htonl(0x1020304);
addr2.sin_addr.s_addr = htonl(0x1010101);

str_ptr = inet_ntoa(addr1.sin_addr);
strcpy(str_arr, str_ptr);
printf("notation1 :%s \n", str_ptr);

inet_ntoa(addr2.sin_addr);
printf("notation2 :%s \n", str_ptr);
printf("notation3 :%s \n", str_arr);

return 0; //
}

/************* 执行结果 ************/
notation1 :1.2.3.4
notation2 :1.1.1.1
notation3 :1.2.3.4

第四章

理解TCP和UDP

根据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDO套接字。因为TCP套接字是面向连接,因此又称为基于流(stream)的套接字。

TCP/IP协议栈

image-20210313103849273

TCP/IP协议栈分4层:链路层、IP层、传输层、应用层

OSI模型分7层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

基于TCP的服务端/客户端

TCP服务端默认函数调用顺序:

  1. socket() 创建套接字
  2. bind() 分配套接字地址
  3. listen() 等待连接请求状态
  4. accept() 允许连接
  5. read()/write() 数据交换
  6. close() 断开连接

TCP客户端默认函数调用顺序

  1. socket()
  2. connect()
  3. read()/write()
  4. close()

函数调用关系

服务器创建套接字后连续调用bind、listen函数进入等待状态,客户端通过调用connect函数发起连接请求。

注意:客户端只有等到服务端调用listen函数以后才能调用connect函数。同时,客户端调用connect函数前,服务端可能率先调用accept函数,当然,磁石服务端在调用accept函数时进入阻塞(blocking)状态,知道客户端调用connect函数为止。

image-20210313180346859

实现迭代服务器端/客户端

实现服务端将客户端传输的字符串数据原封不动的传回客户端。

目前该服务端同一时刻只能服务一个客户端。

迭代回声服务器端/客户端

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
//echo_server.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024

void error_handler(char *message);

int main(int argc, char **argv)
{
int server_sock, client_sock;
char message[BUF_SIZE];
int str_len, i;

struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_size;

if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}

server_sock = socket(PF_INET, SOCK_STREAM, 0);
if (server_sock == -1)
{
error_handler("socket() error");
}

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi(argv[1]));

if (bind(server_sock, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
error_handler("bind() error");
}

if (listen(server_sock, 5) == -1)
{
error_handler("listen() error");
}

client_addr_size = sizeof(client_addr_size);
for (int i = 0; i < 5; i++)
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_addr,
&client_addr_size);
if (client_sock == -1)
{
error_handler("accept() error");
}
else
{
printf("Conected client %d\n", i + 1);
}
while ((str_len = read(client_sock,
message, BUF_SIZE)) != 0)
write(client_sock, message, str_len);
close(client_sock);
}
close(server_sock);

return 0;
}

void error_handler(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
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
//echo_client.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024

void error_handler(char *message);

int main(int argc, char **argv)
{
int sock;
char message[BUF_SIZE];
int str_len = 0;
struct sockaddr_in server_addr;

if (argc != 3)
{
printf("Usage: %s <IP> <PORT>\n", argv[0]);
exit(1);
}

sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
error_handler("socket() error");
}

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));

if (connect(sock, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
error_handler("connect() error");
}
else
{
puts("Connect..............\n");
}

while (1)
{
fputs("Input message(Q to quit):", stdout);
fgets(message, BUF_SIZE, stdin);

if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s\n", message);
}
close(sock);
return 0;
}

void error_handler(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

第五章

第六章

第七章

第八章

-------------THE END-------------

欢迎关注我的其它发布渠道