按照对linux系统的理解,串口编程的顺序无非就是open,read,write,close,而串口有波特率、数据位等重要参数需要设置,因此还应该用到设置函数,那么接下来就带着这几个问题去学习linux下的串口编程。

1、open

  linux串口编程其实也是文件编程,首先要用open函数打开串口设备,获得文件描述符,open函数的简介参照:http://blog.sina.com.cn/s/blog_54f82cc201010oow.html

  首先需要关心的是需要打开的文件名,它肯定是/dev路径下的某个设备,串口设备一般叫做ttyS*ttySAC*,ttyUSB*,我以前就知道这些,后来发现竟然还有叫ttyO*的(当时调试的时候看见/dev目录下有ttyS*的,我就不假思索的,理所当然的代码中写的是ttyS*,结果程序运行时这玩意还成功打开了,但设置的时候就出问题,万万没想到啊,当时用的那个平台ttyS*不是串口,相同目录下还有叫ttyO*的文件,那才是串口)。

  串口设备明显是可读写的,因此传入的第二个参数为O_RDWR。

  因此打开串口1的操作为:

fd = open("/dev/ttySAC1", O_RDWR);

  如果得到的fd不等于-1则表示成功打开串口设备了。

  我在使用过程中遇到过串口无法打开的问题,open返回值为-1。查阅资料后使用命令:sudo chmod 666 /dev/ttyUSB0 修改串口设备权限后就能成功打开了。

  open函数传入的第二个参数一般会用到O_NOCTTY这个标志,它表示阻止操作系统将打开的文件指定为进程的控制终端,如果没有指定这个标志,那么任何一个输入都将会影响用户的进程。我不知道它具体是怎么影响的,但是最好还是加上这个标志。

  此外O_NONBLOCK这个标志也比较常用,它表示以非阻塞模式打开文件,当调用read的时候,如果没有数据也会立即返回-1。有些人会用O_NDELAY这个标志,关于这个标志的解释网上就有很多说法了,我看到就有三种说法:

  1、与O_NONBLOCK一样也是以非阻塞模式打开,但如果没有读取到数据,O_NDELAY返回的是0,而O_NONBLOCK返回的是-1,并且会设置errno为EAGAIN。

  2、O_NDELAY表示这个程序不关心DCD信号线所处的状态,端口的另一端是否激活或者停止。如果用户不指定了这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。

  3、与O_NONBLOCK等价。

  为了探究答案,我在一份linux3.14.38的源代码中搜索,发现这两个标志的值与平台相关。我在Ubuntu中直接用printf打印出来两个标志的值是完全一样的,并且read函数返回的值为都是-1,并不是第一条说的那样O_NDELAY返回的是0。

  我暂时也没有更多的平台去验证,暂且认为网上的那些说法都是基于作者自己正在使用的平台上说的,而在我使用的Ubuntu中O_NDELAY与O_NONBLOCK是完全相同的。

  所以打开串口的操作应为下列2句中的一句:

fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY);
fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);

2、串口设置

  打开串口设备之后还需要对串口进行设置。使用tcsetattr函数设置串口,函数原型为:

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

  成功返回0,失败返回-1。

  第一个参数fd表示打开的串口文件描述符。

  第二个参数optional_actions用于控制修改起作用的时间。可以取下列值:

    TCSANOW:不等数据传输完毕就立即改变属性。
    TCSADRAIN:等待所有数据传输结束才改变属性。
    TCSAFLUSH:清空输入输出缓冲区才改变属性。

  第三个参数struct termios中包含了串口属性,struct termios定义为:

1 #define NCCS 19
2 struct termios {
3 tcflag_t c_iflag; /* input mode flags */
4 tcflag_t c_oflag; /* output mode flags */
5 tcflag_t c_cflag; /* control mode flags */
6 tcflag_t c_lflag; /* local mode flags */
7 cc_t c_line; /* line discipline */
8 cc_t c_cc[NCCS]; /* control characters */
9 };

  看到这种不认识的数据类型就头大,实际上tcflag_t类型就是unsigned int,而cc_t类型就是unsigned char。

  由于struct termios里面的成员太多,因此使用tcgetattr函数先获取到原来的属性,然后再修改我们关心的属性。

  调用tcgetattr函数获取属性,tcgetattr函数原型为:

int tcgetattr(int fd, struct termios *termios_p);

  成功返回0,失败返回-1。

  一般来说串口需要关心的属性为:波特率、数据位、校验位、停止位、流控。下面逐一说明这些数据在哪里改。

  波特率:

    与波特率相关的成员为c_cflag,其中键值(域)CBAUD表示波特率,该域中的位不同的组合表示不同的波特率,表示波特率也用的也是宏,支持的宏及对应的值如下如下:

B0        <==> 0x0000
B50 <==> 0x0001
B75 <==> 0x0002
B110 <==> 0x0003
B134 <==> 0x0004
B150 <==> 0x0005
B200 <==> 0x0006
B300 <==> 0x0007
B600 <==> 0x0008
B1200 <==> 0x0009
B1800 <==> 0x000a
B2400 <==> 0x000b
B4800 <==> 0x000c
B9600 <==> 0x000d
B19200 <==> 0x000e
B38400 <==> 0x000f
B57600 <==> 0x1001
B115200 <==> 0x1002
B230400 <==> 0x1003

    比如将波特率修改为115200的代码就可以这样写:

1 tcgetattr(fd, &termios_uart);
2 termios_uart.c_cflag &= ~CBAUD;
3 termios_uart.c_cflag |= B115200;
4 tcsetattr(fd, TCSANOW, &termios_uart);

    在linux中提供了专门设置波特率的函数,用cfsetispeed和cfsetospeed设置输入输出波特率,还有一个cfsetspeed函数,它们的函数原型为:

       int cfsetispeed(struct termios *termios_p, speed_t speed);

       int cfsetospeed(struct termios *termios_p, speed_t speed);

       int cfsetspeed(struct termios *termios_p, speed_t speed);

    其speed_t类型其实就是unsigned int类型,其取值也正是CBAUD域中可以选择的数据。

    那么设置波特率的代码就变成了这样:

1 tcgetattr(fd, &termios_uart);
2 cfsetspeed(&termios_uart, B115200);
3 tcsetattr(fd, TCSANOW, &termios_uart);

    但是看到这里会有疑问,c_cflag成员中没有将输入波特率和输出波特率分开,这里为什么会有几个不同的设置波特率的函数,这个问题在另一篇博客中仔细探究。

  数据位:

    与数据位相关的成员为c_cflag,其中键值(域)CSIZE表示数据位,与波特率的设置一样,该域中的位不同的组合表示不同的数据位,也可以用宏来表示,支持的宏如下:

  CS5,CS6,CS7,CS8,分别表示数据位为5位、6位、7位、8位。

    将数据位设置为8位就可以这样写:

1 tcgetattr(fd, &termios_uart);
2 termios_uart.c_cflag &= ~CSIZE;
3 termios_uart.c_cflag |= CS8;
4 tcsetattr(fd, TCSANOW, &termios_uart);

  校验位:

    c_cflag中键值PARENB置1表示使用奇偶校验,否则表示不使用校验,在PARENB置1的前提下,键值 PARODD置1表示使用奇校验,否则使用偶校验。

    另外在c_iflag中也有与校验相关的位。如下表:

c_iflag中与校验相关的位
IGNPAR Ignore framing errors and parity errors.
PARMRK If IGNPAR is not set, prefix a character with a parity error or framing error
with \377 \0. If neither IGNPAR nor PARMRK is set, read a character with a par‐
ity error or framing error as \0.
INPCK Enable input parity checking.
ISTRIP Strip off eighth bit.

  停止位:

    c_cflag中键值CSTOPB置1表示使用2个停止位,否则表示使用1个停止位。

  流控:

    c_cflag中键值CRTSCTS置1表示使用硬件流控,否则表示不使用硬件流控。

    软件流控则在c_iflag成员中定义,c_iflag成员中和软件流控相关的键值及解释如下:

IXON Enable software flow control (outgoing)
IXOFF Enable software flow control (incoming)
IXANY Allow any character to start flow again

    所谓流控指的就是控制数据流的流量,如果当接收端接收不了那么多数据时,发送端还在不断的发数据,这样就会出问题。

    硬件流控就是比如A给B发送数据,他们之间除了一根数据线连接以外还有一根流控线,当B将流控线拉高时A才能像B发送数据,当B将流控线拉低时,A就不能发送数据了。

    软件流控没有专门的流控线,如果串口是全双工的,但是如果当 A给B发送数据的时候B不会给A发送数据,那么B就可以将这根数据线用来发送流控数据,当B想要接受数据时,B给A发送IXON,当B不想接受数据时,B给A发送IXOFF。

    理论上来说硬件流控与软件流控可以同时使用,但一般没人会这么做。

  其他设置:

    你以为这样就完了吗,这只是按照裸机开发的思维方式的操作步骤,但这是在linux操作系统上的东西,而且struct termios结构中有这么多成员,这些成员肯定都是有用的啊。

    如果这些东西不设置,程序也许能正常运行,那是因为通过tcgetattr函数获取到了一个默认值,而且这个值是会保存的,下次获取到的值是上次设置的值(无论文件是否关闭,程序是否退出),因此写一份比较健全的代码就至关重要了。

    这里举两个例子说明一下其他参数的重要性,如c_lflag中有一个ECHO标志,该标志置位时串口会将收到的数据回传回去,表现就是你给他发什么东西它就会给你回什么东西。

    另外还有一个函数比较重要,那就是tcflush,tcflush函数原型为:int tcflush(int fd, int queue_selector);  queue_selector的取值和意义如下:

TCIFLUSH 刷新数据接收但不读
TCOFLUSH 刷新数据写入但不传输
TCIOFLUSH 刷新数据接收但不读同时刷新数据写入但不传输

    直接翻译过来非常拗口,理解一下就是无论是即将要发送的数据还是即将要接收的数据,都是放在缓冲区里的,TCIFLUSH意思就是数据已经放在缓冲区里了,但是还没有调用read函数去读,那么直接清空读缓冲区,调用read的时候也接收不到数据。TCOFLUSH的意思就是如果调用了write函数,但是实际上数据还没有开始往外发,那么清空写缓冲区,数据也不发了。

    https://www.cnblogs.com/dartagnan/archive/2013/04/25/3042417.html这篇博客中将里面的成员讲解的非常详细。

  测试程序代码:

  1 /**
2 * filename: uart.c
3 * author: Suzkfly
4 * date: 2021-01-16
5 * platform: Ubuntu
6 * 将USB转串口连接至Ubuntu中,运行程序,能打印串口接收到的数据,也能从终端中获取
7 * 数据发送出去。
8 * 如果不能成功打开设备,使用则ls /dev/ttyUSB0命令看设备是否存在,若存在,则使用
9 * sudo chmod 666 /dev/ttyUSB0 修改文件权限后重新运行程序。
10 */
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <termios.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <string.h>
19
20 /**
21 * \brief define
22 * @{
23 */
24 #define UART_DEV_PATH "/dev/ttyUSB0" /**\brief< 定义打开的串口设备路径 */
25
26 /**
27 * @}
28 */
29
30 /**
31 * \brief 打开串口设备
32 *
33 * \param[in] p_path:设备路径
34 *
35 * \retval 成功返回文件描述符,失败返回-1
36 */
37 int uart_open(const char *p_path)
38 {
39 /* O_NOCTTY:阻止操作系统将打开的文件指定为进程的控制终端,如果没有指定这个标
40 志,那么任何一个输入都将会影响用户的进程。 */
41 /* O_NONBLOCK:使I/O变成非阻塞模式,调用read如果没有接收到数据则立马返回-1,
42 并且设置errno为EAGAIN。*/
43 /* O_NDELAY: 与O_NONBLOCK完全相同 */
44 return open(p_path, O_RDWR | O_NOCTTY);
45 }
46
47 /**
48 * \brief 测试函数,打印struct termios各成员值
49 */
50 static void __print_termios(struct termios *p_termios)
51 {
52 printf("c_iflag = %#08x\n", p_termios->c_iflag);
53 printf("c_oflag = %#08x\n", p_termios->c_oflag);
54 printf("c_cflag = %#08x\n", p_termios->c_cflag);
55 printf("c_lflag = %#08x\n\n", p_termios->c_lflag);
56 }
57
58 /**
59 * \brief 设置串口属性
60 *
61 * \param[in] fd:打开的串口设备的文件描述符
62 * \param[in] baudrate:波特率
63 * #{0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
64 * 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400}
65 * \param[in] bits:数据位
66 * #{5, 6, 7, 8}
67 * \param[in] parity:校验
68 * #'n'/'N':无校验
69 * #'o'/'O':奇校验
70 * #'e','E':偶校验
71 * \param[in] stop:停止位
72 * #1:1个停止位
73 * #2:2个停止位
74 * \param[in] flow:流控
75 * #'n'/'N':不使用流控
76 * #'h'/'H':使用硬件流控
77 * #'s'/'S':使用软件流控
78 *
79 * \retval 成功返回0,失败返回-1,并打印失败原因
80 *
81 * \note 虽然波特率设置支持这么多值,但并不代表输入表中支持的值波特率就
82 * 一定能设置成功。
83 */
84 int uart_set(int fd, int baudrate, int bits, char parity, int stop, char flow)
85 {
86 struct termios termios_uart;
87 int ret = 0;
88 speed_t uart_speed = 0;
89
90 /* 获取串口属性 */
91 memset(&termios_uart, 0, sizeof(termios_uart));
92 ret = tcgetattr(fd, &termios_uart);
93 if (ret == -1) {
94 printf("tcgetattr failed\n");
95 return -1;
96 }
97
98 //__print_termios(&termios_uart);
99
100 /* 设置波特率 */
101 switch (baudrate) {
102 case 0: uart_speed = B0; break;
103 case 50: uart_speed = B50; break;
104 case 75: uart_speed = B75; break;
105 case 110: uart_speed = B110; break;
106 case 134: uart_speed = B134; break;
107 case 150: uart_speed = B150; break;
108 case 200: uart_speed = B200; break;
109 case 300: uart_speed = B300; break;
110 case 600: uart_speed = B600; break;
111 case 1200: uart_speed = B1200; break;
112 case 1800: uart_speed = B1800; break;
113 case 2400: uart_speed = B2400; break;
114 case 4800: uart_speed = B4800; break;
115 case 9600: uart_speed = B9600; break;
116 case 19200: uart_speed = B19200; break;
117 case 38400: uart_speed = B38400; break;
118 case 57600: uart_speed = B57600; break;
119 case 115200: uart_speed = B115200; break;
120 case 230400: uart_speed = B230400; break;
121 default: printf("Baud rate not supported\n"); return -1;
122 }
123 cfsetspeed(&termios_uart, uart_speed);
124
125 /* 设置数据位 */
126 switch (bits) {
127 case 5: /* 数据位5 */
128 termios_uart.c_cflag &= ~CSIZE;
129 termios_uart.c_cflag |= CS5;
130 break;
131
132 case 6: /* 数据位6 */
133 termios_uart.c_cflag &= ~CSIZE;
134 termios_uart.c_cflag |= CS6;
135 break;
136
137 case 7: /* 数据位7 */
138 termios_uart.c_cflag &= ~CSIZE;
139 termios_uart.c_cflag |= CS7;
140 break;
141
142 case 8: /* 数据位8 */
143 termios_uart.c_cflag &= ~CSIZE;
144 termios_uart.c_cflag |= CS8;
145 break;
146
147 default:
148 printf("Data bits not supported\n");
149 return -1;
150 }
151
152 /* 设置校验位 */
153 switch (parity) {
154 case 'n': /* 无校验 */
155 case 'N':
156 termios_uart.c_cflag &= ~PARENB;
157 termios_uart.c_iflag &= ~INPCK; /* 禁能输入奇偶校验 */
158 break;
159
160 case 'o': /* 奇校验 */
161 case 'O':
162 termios_uart.c_cflag |= PARENB;
163 termios_uart.c_cflag |= PARODD;
164 termios_uart.c_iflag |= INPCK; /* 使能输入奇偶校验 */
165 termios_uart.c_iflag |= ISTRIP; /* 除去第八个位(奇偶校验位) */
166 break;
167
168 case 'e': /* 偶校验 */
169 case 'E':
170 termios_uart.c_cflag |= PARENB;
171 termios_uart.c_cflag &= ~PARODD;
172 termios_uart.c_iflag |= INPCK; /* 使能输入奇偶校验 */
173 termios_uart.c_iflag |= ISTRIP; /* 除去第八个位(奇偶校验位) */
174 break;
175
176 default:
177 printf("Parity not supported\n");
178 return -1;
179 }
180
181 /* 设置停止位 */
182 switch (stop) {
183 case 1: termios_uart.c_cflag &= ~CSTOPB; break; /* 1个停止位 */
184 case 2: termios_uart.c_cflag |= CSTOPB; break; /* 2个停止位 */
185 default: printf("Stop bits not supported\n");
186 }
187
188 /* 设置流控 */
189 switch (flow) {
190 case 'n':
191 case 'N': /* 无流控 */
192 termios_uart.c_cflag &= ~CRTSCTS;
193 termios_uart.c_iflag &= ~(IXON | IXOFF | IXANY);
194 break;
195
196 case 'h':
197 case 'H': /* 硬件流控 */
198 termios_uart.c_cflag |= CRTSCTS;
199 termios_uart.c_iflag &= ~(IXON | IXOFF | IXANY);
200 break;
201
202 case 's':
203 case 'S': /* 软件流控 */
204 termios_uart.c_cflag &= ~CRTSCTS;
205 termios_uart.c_iflag |= (IXON | IXOFF | IXANY);
206 break;
207
208 default:
209 printf("Flow control parameter error\n");
210 return -1;
211 }
212
213 /* 其他设置 */
214 termios_uart.c_cflag |= CLOCAL; /* 忽略modem(调制解调器)控制线 */
215 termios_uart.c_cflag |= CREAD; /* 使能接收 */
216
217 /* 禁能执行定义(implementation-defined)输出处理,意思就是输出的某些特殊数
218 据会作特殊处理,如果禁能的话那么就按原始数据输出 */
219 termios_uart.c_oflag &= ~OPOST;
220
221 /**
222 * 设置本地模式位原始模式
223 * ICANON:规范输入模式,如果设置了那么退格等特殊字符会产生实际动作
224 * ECHO:则将输入字符回送到终端设备
225 * ECHOE:如果ICANON也设置了,那么收到ERASE字符后会从显示字符中擦除一个字符
226 * 通俗点理解就是收到退格键后显示内容会往回删一个字符
227 * ISIG:使终端产生的信号起作用。(比如按ctrl+c可以使程序退出)
228 */
229 termios_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
230
231 /**
232 * 设置等待时间和最小接收字符
233 * 这两个值只有在阻塞模式下有意义,也就是说open的时候不能传入O_NONBLOCK,
234 * 如果经过了c_cc[VTIME]这么长时间,缓冲区内有数据,但是还没达到c_cc[VMIN]个
235 * 数据,read也会返回。而如果当缓冲区内有了c_cc[VMIN]个数据时,无论等待时间
236 * 是否到了c_cc[VTIME],read都会返回,但返回值可能比c_cc[VMIN]还大。如果将
237 * c_cc[VMIN]的值设置为0,那么当经过c_cc[VTIME]时间后read也会返回,返回值
238 * 为0。如果将c_cc[VTIME]和c_cc[VMIN]都设置为0,那么程序运行的效果与设置
239 * O_NONBLOCK类似,不同的是如果设置了O_NONBLOCK,那么在没有数据时read返回-1,
240 * 而如果没有设置O_NONBLOCK,那么在没有数据时read返回的是0。
241 */
242 termios_uart.c_cc[VTIME] = 1; /* 设置等待时间,单位1/10秒 */
243 termios_uart.c_cc[VMIN] = 1; /* 最少读取一个字符 */
244
245 tcflush(fd, TCIFLUSH); /* 清空读缓冲区 */
246
247 /* 写入配置 */
248 ret = tcsetattr(fd, TCSANOW, &termios_uart);
249 if (ret == -1) {
250 printf("tcsetattr failed\n");
251 }
252
253 return ret;
254 }
255
256
257 /**
258 * \test
259 * @{
260 */
261 int main(int argc, const char *argv[])
262 {
263 int fd = 0;
264 int ret = 0;
265 int pid = 0;
266 char buf[128] = { 0 };
267 int len = 0;
268 int i = 0;
269
270 /* 打开串口设备 */
271 fd = uart_open(UART_DEV_PATH);
272 if (fd < 0) {
273 printf("open %s failed\n", UART_DEV_PATH);
274 return 0;
275 }
276
277 /**
278 * 配置串口:
279 * 波特率:9600
280 * 数据位:8
281 * 校验 :无校验
282 * 停止位:1
283 * 流控 :无流控
284 */
285 ret = uart_set(fd, 9600, 8, 'n', 1, 'n');
286 if (ret == -1) {
287 return 0;
288 }
289
290 pid = fork();
291 if (pid > 0) { /* 读数据 */
292 while (1) {
293 memset(buf, 0, sizeof(buf));
294 len = read(fd, buf, sizeof(buf));
295 if (len > 0) {
296 printf("len: %d\n", len);
297 printf("data: ");
298 #if 0 /* 十六进制打印 */
299 for (i = 0; i < len; i++) {
300 printf("%02x ", buf[i]);
301 }
302 printf("\n\n");
303 #else /* 字符串打印 */
304 printf("%s\n\n", buf);
305 #endif
306 } else {
307 printf("len = %d\n", len);
308 }
309 }
310 } else if (pid == 0) { /* 写数据 */
311 while (1) {
312 scanf("%s", buf);
313 write(fd, buf, strlen(buf));
314 }
315 } else {
316 printf("fork failed\n");
317 }
318
319 return 0;
320 }
321
322 /**
323 * @}
324 */

    

  

linux串口编程的更多相关文章

  1. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

  2. Linux串口编程详解(转)

    串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...

  3. linux串口编程总结

    串口本身.标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口以前被广泛用于连接计算机和终端设备和各种外部设备.尽管以太网接口和USB接口也是以一个串行流进行数据传送的.可是串口连接 ...

  4. linux串口编程参数配置详解(转)

    1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义#include <stdlib.h>        //标准函数 ...

  5. linux串口编程参数配置详解

    1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义 #include <stdlib.h>        //标准函 ...

  6. Linux串口编程进阶

    在<Linux串口编程>编程一文中介绍了串口应用中常用的基本操作,如:串口打开关闭.串口设置.数据收发等.本篇文章主要基于常规串口操作进行了扩充,主要介绍如下操作: Linux系统使用非标 ...

  7. Linux串口编程(转载)

    在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串口编程时 ...

  8. linux串口编程设置(转载)

    (转载)在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串 ...

  9. Linux 串口编程

    今天对应用层串口编程进行了验证.程序来源于以下参考链接,自己进行了一些注释和更改,记录于此. Tony Liu, 2016-6-17, Shenzhen 参考链接 https://www.ibm.co ...

随机推荐

  1. CSS—— div+css

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. 简丽Framework-开篇

    简丽Framework-开篇 ​ 简丽Framework 是一个开源java Web开发框架. ​ 开源的框架.库.组件等比比皆是,每个开源产品都有它的定位和价值. ​ 简丽Framework的定位是 ...

  3. jq再次封装自己的ajax & js 回调函数 & js方法注释&js 全局屏蔽点击事件及a标签

    1.封装成一个独立JS var commonUrl = 'http://xx.xxx.com/'; function http({ url, type = "post", data ...

  4. 无法启动IIS Express Web服务器

    解决 打开项目文文件夹以.csproj结尾的文件),找到WebProjectProperties节点然后,将图中框选的三项节点内容全部清空 删除.vs 重新启动即可

  5. H3C路由器配置示列一

    说明: 如上图所示,192.168.0.0网段是总公司的网络,192.168.3.0和192.168.4.0网段是当前自己分公司的网络,且192.168.3.0是内网不能访问互联网,192.168.4 ...

  6. 深入理解MySQL系列之redo log、undo log和binlog

    事务的实现 redo log保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能. InnoDB存储引擎体系结构 redo log Write Ahead Log策略 事务提交时,先写重 ...

  7. 开发规范(三)数据库 By 阿里

    建表规约 索引规约 SQL语句 ORM映射

  8. NIO 的工作方式

    NIO 的工作方式 BIO 带来的挑战 BIO : BIO 通信模型,通常由一个独立的 Acceptor 线程负责监听客户端的连接,接受到请求之后,为每个客户端创建一个新的线程进行链路处理,处理完成之 ...

  9. double 转为long类型

    System.out.println(new Double(234314.999999999).longValue());//234314  System.out.println(new Double ...

  10. springboot项目中使用jsp

    在pom文件中 1.方法一 <!-- 引入tomcate内嵌的jsp解析包--> <dependency> <groupId>org.apache.tomcat.e ...