netstat命令help翻译

翻译了一下 netstat –help 的提示信息

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
zzz@lengzzz:~$ netstat --help
usage: netstat [-vWeenNcCF] [<Af>] -r netstat {-V|--version|-h|--help}
netstat [-vWnNcaeol] [<Socket> ...]
netstat { [-vWeenNac] -i | [-cWnNe] -M | -s }

-r, --route 显示路由表
-i, --interfaces 显示接口列表
-g, --groups 显示组播组成员
-s, --statistics 显示网络统计数据(类似SNMP)
-M, --masquerade 显示伪装连接

-v, --verbose 显示详细信息
-W, --wide 不截断IP地
-n, --numeric 不解析名字
--numeric-hosts 不解析主机名
--numeric-ports 不解析端口名
--numeric-users 不解析用户名
-N, --symbolic 解析硬件名
-e, --extend 显示更多信息
-p, --programs 显示套接字的PID/程序名
-c, --continuous 刷屏显示

-l, --listening 显示监听套接字
-a, --all, --listening 显示所有套接字(默认:只显示连接套接字)
-o, --timers 显示套接字定时器状态
-F, --fib 显示前导路由信息(默认启用)
-C, --cache 显示路由cache中的前导路由信息

<Socket>={-t|--tcp} {-u|--udp} {-w|--raw} {-x|--unix} --ax25 --ipx --netrom
<AF>=Use '-6|-4' or '-A <af>' or '--<af>'; default: inet
可以使用的地址族列表 (支持路由的):
inet (DARPA Internet) inet6 (IPv6) ax25 (AMPR AX.25)
netrom (AMPR NET/ROM) ipx (Novell IPX) ddp (Appletalk DDP)
x25 (CCITT X.25)

exec系列系统调用

exec系列系统调用有六个,本文讲解了它们之间的区别

exec系列系统调用有六个,通过后缀字母的含义很好记忆:

  • execl
  • execlp
  • execle
  • execv
  • execvp
  • execve

l 代表参数是一个list(用NULL结尾的可变参数),v 代表参数是一个vector(char* const argv[])。
p 代表调用时可以只提供文件名,只要该文件在系统变量PATH中
e 代表为新进程提供了一个新的环境

原型如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

上面的都是c函数,只有execve是系统调用。另外多出的一个execvpe是linux特有的,只有glibc库才有

1
2
3
4
#include <unistd.h>

int execve(const char *filename, char *const argv[],
char *const envp[]);

unix网络编程中的图给出了他们之间的关系:

image_1bl0edi351pei15d8vba25occg9.png-96.5kB

算是课后作业吧…

功能:聊天室服务器,可用telnet直接连接到服务器,发送消息则在聊天室内的所有用户都能看见。使用select实现

功能:聊天室服务器

可用telnet直接连接到服务器,发送消息则在聊天室内的所有用户都能看见。

使用select实现

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#define max(a, b) ((a) > (b) ? (a) : (b))

struct filedescriptor_node
{
int fd;
struct filedescriptor_node* next;
};

struct filedescriptor_set
{
size_t len;
struct filedescriptor_node* head;
struct filedescriptor_node* iter;
int max_fd;

fd_set fds;
};

int fds_init(struct filedescriptor_set* _this)
{
bzero(_this, sizeof(struct filedescriptor_set));

FD_ZERO(&_this->fds);
return 0;
}

struct filedescriptor_set*
fds_create()
{
struct filedescriptor_set* _this;
_this = malloc(sizeof(struct filedescriptor_set));
if (_this == NULL)
return NULL;

if (fds_init(_this) == -1)
{
free(_this);
return NULL;
}
return _this;
}

int fds_add(struct filedescriptor_set* _this, int fd)
{
struct filedescriptor_node* p;
p = malloc(sizeof(struct filedescriptor_node));
if (p == NULL)
return -1;
p->fd = fd;
p->next = _this->head;
_this->head = p; /* 在head前插入新节点 */
++_this->len;

if (fd > _this->max_fd)
_this->max_fd = fd;

FD_SET(fd, &_this->fds);

return 0;
}

int fds_remove(struct filedescriptor_set* _this, int fd)
{
struct filedescriptor_node* p;
if (_this->head->fd == fd)
{
p = _this->head;
_this->head = p->next;
free(p);
}
else
{
for (p = _this->head; p->next != NULL; p = p->next)
if (p->next->fd == fd)
break; /* 令p指向fd的前一个 */
if (p->next == NULL)
return -1;
}
--_this->len;

if (fd == _this->max_fd)
{
_this->max_fd = 0; /* _this->max_fd = _this->head->fd不正确,
this->head可能为NULL */
for (p = _this->head; p != NULL; p = p->next)
if (p->fd > _this->max_fd)
_this->max_fd = p->fd;
}

FD_CLR(fd, &_this->fds);

return 0;
}

fd_set fds_getfdset(struct filedescriptor_set* _this)
{
return _this->fds;
}

int fds_getmaxfd(struct filedescriptor_set* _this)
{
return _this->max_fd;
}

void fds_iter_init(struct filedescriptor_set* _this)
{
_this->iter = _this->head;
}

int fds_iter_next(struct filedescriptor_set* _this)
{
if (_this->iter != NULL)
{
int fd = _this->iter->fd;
_this->iter = _this->iter->next;
return fd;
}
else
return -1;
}

void fds_destroy(struct filedescriptor_set* _this)
{
struct filedescriptor_node *p, *q;
p = _this->head;
q = p->next;
while (p != NULL)
{
free(p);
p = q;
if (q != NULL)
q = q->next;
}
}

void fds_delete(struct filedescriptor_set* _this)
{
fds_destroy(_this);
free(_this);
}

int writen(int fd, char* buf, size_t len)
{
ssize_t n;
size_t left = len;

while (left > 0)
{
if ((n = write(fd, buf, len)) <= 0)
{
if (errno == EINTR)
continue;
else
return -1;
}
left -= n;
buf += n;
}
return len; /* 无论如何也要写完,否则就是错误 */
}

int readn(int fd, char* buf, size_t len)
{
ssize_t n;
size_t left = len;

while (left > 0)
{
if ((n = read(fd, buf, len)) < 0)
{
if (errno == EINTR)
continue;
else
return -1;
}
else if (n == 0) /* 到达EOF */
{
break;
}
left -= n;
buf += n;
}
return len - left; /* 可能提前到达EOF */
}

#define PORT 10086
#define BUF_SIZE 4096

typedef struct sockaddr SA;
int main(int argc, char* argv[])
{
struct filedescriptor_set fds;
int svrsock;
struct sockaddr_in svraddr;

svrsock = socket(AF_INET, SOCK_STREAM, 0);
if (svrsock == -1)
{
perror("socket");
exit(errno);
}
bzero(&svraddr, sizeof(svraddr));
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(PORT);
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(svrsock, (SA*)&svraddr, sizeof(svraddr)) == -1)
{
perror("bind");
exit(errno);
}
if (listen(svrsock, 64) == -1)
{
perror("listen");
exit(errno);
}

if (fds_init(&fds) == -1)
{
perror("fds_init");
exit(errno);
}

while (1)
{
int fd;
fd_set fdset;
char buf[BUF_SIZE];
size_t buflen;

fdset = fds_getfdset(&fds);
FD_SET(svrsock, &fdset);
if (select(max(fds_getmaxfd(&fds), svrsock) + 1, &fdset, NULL, NULL, NULL) == -1)
{
perror("select");
exit(errno);
}
if (FD_ISSET(svrsock, &fdset))
{
int cltsock;
struct sockaddr_in cltaddr;
socklen_t cltaddrlen = sizeof(cltaddr);
char cltaddrstr[INET_ADDRSTRLEN];

if ((cltsock = accept(svrsock, (SA*)&cltaddr, &cltaddrlen)) == -1)
{
perror("accept");
exit(errno);
}
if (inet_ntop(AF_INET, &cltaddr.sin_addr, cltaddrstr, INET_ADDRSTRLEN) == NULL)
{
perror("inet_ntop");
exit(errno);
}
/* 通知新用户到达 */
printf("incoming connection from %s:%d\n", cltaddrstr, ntohs(cltaddr.sin_port));
snprintf(buf, BUF_SIZE, "user %s:%d joined\n", cltaddrstr, ntohs(cltaddr.sin_port));
buflen = strlen(buf);
for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;)
{
if (writen(fd, buf, buflen) == -1)
{
perror("writen");
exit(errno);
}
}
strncpy(buf, "Welcom\n", BUF_SIZE);
buflen = strlen(buf);
if (writen(cltsock, buf, buflen) == -1)
{
perror("writen");
exit(errno);
}

if (fds_add(&fds, cltsock) == -1)
{
perror("fds_add");
exit(errno);
}

}
for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;)
{
if (FD_ISSET(fd, &fdset))
{
int n;
struct filedescriptor_node* old;
if ((n = readn(fd, buf, BUF_SIZE)) == -1)
{
perror("readn");
exit(errno);
}
else if (n == 0)
{
/*struct sockaddr_in cltaddr;
socklen_t cltaddrlen = sizeof(cltaddr);
char cltaddrstr[INET_ADDRSTRLEN];
if (getpeername(fd, (SA*)&cltaddr, &cltaddrlen) == -1)
{
perror("getpeername");
exit(errno);
}
if (inet_ntop(AF_INET, &cltaddr.sin_addr, cltaddrstr, INET_ADDRSTRLEN) == NULL)
{
perror("inet_ntop");
exit(errno);
}

printf("%s disconnected the connection\n", cltaddrstr);
snprintf(buf, BUF_SIZE, "user %s exited\n", cltaddrstr);
buflen = strlen(buf);
for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;)
{
if (writen(fd, buf, buflen) == -1)
{
perror("writen");
exit(errno);
}
}*/

fds_remove(&fds, fd);
continue;
}
old = fds.iter;
for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;)
{
if (writen(fd, buf, n) == -1)
{
perror("writen");
exit(errno);
}
}
fds.iter = old;
}
}

}

fds_destroy(&fds);
return 0;
}

select就绪条件

select函数当被检测的文件描述符可读或可写或异常时返回. 可是究竟什么可以称作可读什么叫可写什么叫异常? 到达EOF算是哪一种?

unix网络编程中给出了详细解释:

image_1bl0e2qg7f8r1he3n4p1lh510ec9.png-406.5kB

image_1bl0e35vmqrg1oie1bohovt16qdm.png-158.1kB

简言之, 描述符可读就是以下情况:

  • 有数据传过来
  • 对端关闭连接(到达EOF)
  • 监听套接字收到连接请求
  • 发生套接字错误

描述符可写是以下情况

  • 缓冲区空闲可写
  • 写半部关闭
  • connect建立连接完成
  • 发生套接字错误

描述符异常只有一种情况: 有带外数据到达.

表格6.7更加清晰

unix网络编程中常用头文件、结构体、函数

unix网络编程中常用头文件、结构体、函数

1.常用头文件

<sys/socket.h>

socket, bind, connect等函数定义, 所有socket程序必须要包含, 另外定义了一些通用的套接字地址结构, 如struct sockaddr.

<netinet/in.h>

struct sockaddr_in, struct sockaddr_in6等结构体的定义, 定义了ip协议中的套接字地址结构. 另外有些基础类型定义in_addr_t, in_port定义. hton, ntoh 字节序转换函数.

<arpa/inet.h>

inet_pton, inet_ntop, ip地址转换函数.

2.部分函数原型与结构体定义

1)ipv4套接字地址结构体定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* from <netinet/in.h> */
/* Internet address. */
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. */

/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};

《unix网络编程》上的定义:

1
2
3
4
5
6
7
8
9
10
11
struct in_addr {
in_addr_t s_addr;
};

struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

2)POSIX规范定义的类型:

image_1bl0dmjqfum819j74jn19na1ecv9.png-77.2kB

3)通用的套接字地址:

1
2
3
4
5
struct sockaddr {
uint_8 sa_len;
sa_family_t sa_family;
char sa_data[14];
};

4)各种套接字地址结构体:

image_1bl0dnjs71238jhiqo21omc17s5m.png-187.3kB

5)字节序函数:

1
2
3
4
5
6
#include <netinet/in.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

6)ip地址转换函数:

1
2
3
4
5
6
#include <sys/types.h>
#include <sys/socket.h>;
#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);

这两个函数传入(或传出)的ip地址结构体为struct in_addr等结构体

传出的字符串buffer大小在<netinet/in.h>中定义:

1
2
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 48

7)套接字函数:

1
2
3
4
5
6
7
8
9
10
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
int listen(int s, int backlog);
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
int getpeername(int s, struct sockaddr *name, socklen_t *namelen);

3.常见用法

1)初始化套结字地址结构:

对于IPV4一般需要初始化三个字段:sin_family、sin_addr、sin_port。

1
2
3
4
5
6
7
8
9
bzero(&svraddr, sizeof(svraddr));/* 注意清零 */
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(PORT);/* 注意大端小端的转换 */
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);/* 设置ip地址为任意地址 */
if (inet_pton(AF_INET, "192.168.0.1", &addr.sin_addr) < 0)/* 使用inet_pton设置ip */
{
perror("inet_pton");
exit(errno);
}

2)创建套结字:

1
2
3
4
5
6
svrsock = socket(AF_INET, SOCK_STREAM, 0);/* 参数3可以推断得出 */
if (svrsock == -1)
{
perror("socket");
exit(errno);
}

3)绑定套结字到套结字地址:

1
2
3
4
5
if (bind(svrsock, (SA*)&svraddr, sizeof(svraddr)) == -1)
{
perror("bind");
exit(errno);
}

4)连接套结字到对端:

1
2
3
4
5
if (connect(svrsock, (SA*)peeraddr, sizeof(peeraddr)) == -1)
{
perror("connect");
exit(errno);
}

5)设置套结字为监听模式:

1
2
3
4
5
if (listen(svrsock, 64) == -1)
{
perror("listen");
exit(errno);
}

6)从套结字上等待一个连接:

1
2
3
4
5
6
7
8
int cltsock;
struct sockaddr_in cltaddr;
socklen_t cltaddrlen = sizeof(cltaddr);
if ((cltsock = accept(svrsock, &cltaddr, &cltaddrlen)) == -1)
{
perror("accept");
exit(errno);
}

7)获取本端和对端套结字地址:

1
2
3
4
5
6
7
8
9
10
11
12
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(svrsock, &addr, &addrlen) == -1)
{
perror("getsockname");
exit(errno);
}
if (getpeername(svrsock, &addr, &addrlen) == -1)
{
perror("getpeername");
exit(errno);
}

8)将ip地址转换为字符串形式

1
2
3
4
5
6
char cltaddrstr[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &cltaddr.sin_addr, cltaddrstr, INET_ADDRSTRLEN) == NULL)/* 注意为cltaddr.sin.addr */
{
perror("inet_ntop");
exit(errno);
}

9)将字符串转成ip地址

1
2
3
4
5
if (inet_pton(AF_INET, "192.168.1.1", &cltaddr.sin_addr) == -1)
{
perror("inet_pton");
exit(errno);
}

电脑应用low技巧

电脑应用low技巧

没办法,人家太low了,不像蛋蛋”手熟尔”,所以只能记下了

netplwiz 实现自动登录;

msconfig 设置启动项目;

at 22:00 shutdown -s 定时关机 如果取消关机则需 shutdown/a即可;–蛋蛋教我的是 直接 shutdown 7200 -t -s

ALT +TAB 切换窗口;

gpedit.msc 设置禁止访问控制面板 & 其他…..如限制用户访问某个磁盘分区;

全选文件可以批量修改文件名,用文件扩展名批量修改器也可修改扩展名;

删除文件时不再出现确认对话框& 进入回收站的文件直接彻底删除 :右击回收站 属性设置即可;

使用EasyRecovery找回丢失的文件 高强度文件夹加密大师可以加密重要文件;

c:用户../temp 清除系统临时文件夹;

格式刷;

word 中,选择 审阅->批注 可以增添批注;

审阅他人文档时,采用修订的方法可以既修改也让他人看到修改了哪些内容 :切换到审阅选项卡,单击修订,修改部分会以红色显示;

批量下载qq相册中的相片:批量下载器;

image_1bl0d7dpn1akd1um2g5l146315sm9.png-52.6kB

Quartus II开发工具的基本操作

Quartus II开发工具的基本操作

1 新建工程 New project wizard

2 file-new 编写源码,保存源文件.选择Processing->start->start analysis&synthesis 或者单击对应图标image_1bl0cvqbj1cp8m9miju13lffdg9.png-0.3kB

3 Tools-Netlist Viewers – RTL Viewer 查看生成综合后的电路结构图

4功能仿真: Processing->Generate Functional Simulation Netlist产生功能网络表;

File->new 新建VWF向量波形文件. 右击 Insert ->Insert Node or Bus 加入结点或总线,进行结点或总线信号的查找,选择

需要仿真观察的信号,进行信号设置;

保存文件;

单击菜单选项image_1bl0d0ep0v0r1tfg1ddhh5ev2m.png-0.2kB进行仿真设置,如

image_1bl0d0r7u1nb31f0v1kl719vgic213.png-27kB

单击工具按钮image_1bl0d1p0hbqh11anq6k9u717io2g.png-0.3kB进行功能仿真.

5时序仿真 需要对整个工程进行全编译,image_1bl0d28ne1pp711i3gj2toc10ba2t.png-0.3kB,编译无误后

修改image_1bl0d2o351ca6qcp1b7hkkq41e3a.png-26.9kB为timing模式

单击image_1bl0d33qb1upr1836mr01br11umg3n.png-0.3kB,进行时序仿真.

Quartus II 与ModelSim联合开发

源文件 测试文件编写完毕;

单击https://hzzz.lengzzz.com/blog/wp-content/uploads/2014/01/QQ%E6%88%AA%E5%9B%BE20140105164431.png![image_1bl0d44n11p6r1aob1guvj9gp44.png-0.2kB][8],选择EDA Tools Setting -> Simulation进行相关仿真设置.

设置完毕后,进行工程编译,无误后选择Tools ->Run EDA Simulation tool ->EDA RTL simulation 调用modelsim进行仿真.

如何焊接贴片IC

看到很好用的如何焊接贴片IC, 果断收藏之供蛋蛋和我下次使用

1 先在PCB上固定IC的一个脚

image_1bl05r33j97o1nro1ntq10ek1vl49.png-568.4kB

2 堆满脚如图

image_1bl05rd8hlvrvqo1o03v60m11m.png-508.1kB

3 找细铜丝和松香

image_1bl05ron71fae1lo9lj01638in013.png-295.9kB

4 放在IC脚上,用铜丝吸锡

image_1bl05s2sa1m7kp8oevv1ouf1kv01g.png-543.9kB

5 用酒精清洗(用棉签)

image_1bl05sc9s1fq21a1m11i0ne71mjg1t.png-548.1kB

ppp协议

ppp协议详解

1.概述

ppp协议分为几个部分:LCP(链路控制协议)、NCP(网络控制协议)、认证协议(包括PAP协议和CHAP协议)、另外还有CCP(压缩控制协议)。

如下图所示:

image_1bl05ebacvgpurr6fpo7neq79.png-186.3kB

ppp是一个分层结构。在底层,它能使用同步媒介(如ISDNH或同步DDN专线),也能使用异步媒介(如基于Modem拨号的PSTN网络)。

在数据链路层,PPP在链路层建立方面提供了丰富的服务,这些服务以LCP协商选项的形式提供。

在上层,PPP通过NCPs提供对多种网络层协议的支持。PPP对于每一种网络层协议都有一种封装格式来区别它们的报文。

2.帧结构

基本的ppp帧如下:

image_1bl05fa3u39duuo146u5bl194em.png-27.9kB

标记用于标记一个ppp帧的开始和结束。

地址域和控制域为固定值(0xff,0x03)

协议的可选值如下:

1
2
3
4
5
6
7
8
9
10
11
协议代码          对应协议

0XC021 LCP协议

0XC023 PAP协议

0XC223 CHAP协议

0X8021 IPCP协议

0X0021 IP协议

3.建立连接
PPP协商过程分为几个阶段:Dead阶段,Establish阶段,Authenticate阶段,Network阶段和Termintate阶段,在不同的阶段进行不同协议的协商.只有前面的协商出现结果后,才能转到下一个阶段,进行下一个协议的协商.

image_1bl05h1551vu94i197egm118ie13.png-196.8kB

  1. 当物理层不可用时,PPP链路处于dead阶段,链路必须从这个阶段开始和结束.当物理层可用时,PPP在建立链路之前首先进行LCP协商,协商内容包括工作方式是SP还是MP,验证方式和最大传输单元等.
  2. LCP协商过后就进入Establish阶段,此时LCP状态为Opened,表示链路已经建立.
  3. 如果培植了验证(远端验证本地或者本地验证远端)就进入Authenticate阶段,开始CHAP或PAP验证.
  4. 如果验证失败进入Terminate阶段,拆除链路,LCP状态转为Down;如果验证成功就进入Network协商阶段(NCP),此时LCP状态仍为Opened,而IPCP状态从Initial转到Request.
  5. NCP协商支持IPCP协商,IPCP协商主要包括双方的IP地址.通过NCP协商来选择和配置一个网络层协议.当选中的网络层协议配置成功后,该网络层协议就可以通过这条链路发送报文了.
  6. PPP链路将一直保持通信,直至有明确的LCP或NCP帧关闭这条链路,或发生了某些外部事件.(例如,用户的干预).

在建立连接时需要用到LCP和NCP协议(IPCP)。

这些协议都会用到编码和ID字段(其实也可以这样分,地址、协议、控制三个字段属于ppp协议,后面的属于LCP或NCP或IP自己的协议)

编码字段的含义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
编码值	        对应含义

1 配置请求(Req)

2 接受配置(Ack)

3 配置请求接受,其他拒绝(Nak)

4 配置请求不认识或不被接受(Rej)

5 终止链接

6 终止确认

传输IP报文时,没有编码和ID字段,在协议字段之后直接就是IP报文。

4.验证

PAP验证,没啥好说的,客户端向服务器发送明文用户名和密码,服务器发回Acknowledge或Not Acknowledge

image_1bl05jtp3qs1148f13051c0d18491g.png-187.3kB

CHAP验证,比PAP验证安全,不明文发送密码。具体流程是这样:

  1. 服务器向客户端发送一个随机生成的质询字符串(challenge string)和自己的主机名。
  2. 客户端用自己的密码作为秘钥,通过单向加密算法(HASH)对服务器发来的质询字符串进行加密,之后将自己的用户名(明文)和加密后的质询字符串发送到服务器。
  3. 服务器通过收到的用户名查询该用户的密码,用这个密码作为秘钥,也对刚刚的质询字符串进行单向加密,如果和用户发来的加密质询字符串一致的话,返回ACK,否则返回NAK。

image_1bl05kjrq19ln1tdg9fb1k4h159a1t.png-205.7kB

更明了的图如下:

image_1bl05krd67doh1ujmh5kkga72a.png-131.7kB

Proudly powered by Hexo and Theme by Hacker
© 2021 wastecat