Skip to content

Commit cc87557

Browse files
committed
P110 - 2019年8月28日23:24:52
1 parent 7e977f3 commit cc87557

File tree

3 files changed

+251
-5
lines changed
  • 5. 代码清单6-3 用sendfile函数传输文件
  • 6. 代码清单6-4 用splice函数实现的回射服务器

3 files changed

+251
-5
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <sys/types.h>
2+
#include <sys/socket.h>
3+
#include <netinet/in.h>
4+
#include <arpa/inet.h>
5+
#include <assert.h>
6+
#include <string.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <unistd.h>
10+
#include <errno.h>
11+
#include <sys/sendfile.h>
12+
#include <fcntl.h>
13+
#include <sys/stat.h>
14+
15+
#define BUFFER_SIZE 1024
16+
17+
static const char* status_line[2] = {"200 OK", "500 Server Error"};
18+
19+
int main(int argc, char* argv[]) {
20+
21+
if(argc <= 3) {
22+
printf("Wrong number of parameters\n");
23+
return 1;
24+
}
25+
26+
char* ip = argv[1];
27+
int port = atoi(argv[2]);
28+
char* file_name = argv[3];
29+
30+
// ¼û±Ê¼Ç²¿·Ö
31+
int filefd = open(file_name, O_RDONLY);
32+
assert(filefd > 0);
33+
struct stat stat_buf;
34+
fstat(filefd, &stat_buf);
35+
36+
37+
struct sockaddr_in address;
38+
memset(&address, 0, sizeof(address));
39+
address.sin_family = AF_INET;
40+
address.sin_port = htons(port);
41+
inet_pton(AF_INET, ip, &address.sin_addr);
42+
43+
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
44+
assert(sockfd >= 0);
45+
int retu = bind(sockfd, (struct sockaddr*)&address, sizeof(address));
46+
assert(retu != -1);
47+
retu = listen(sockfd, 5);
48+
assert(sockfd != -1);
49+
50+
struct sockaddr_in client;
51+
socklen_t client_addrlength = sizeof(client);
52+
int connfd = accept(sockfd, (struct sockaddr*)&client, &client_addrlength);
53+
if(connfd >= 0) {
54+
55+
sendfile(connfd, filefd, NULL, stat_buf.st_size);
56+
close(connfd);
57+
}
58+
close(sockfd);
59+
return 0;
60+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include <sys/socket.h>
2+
#include <sys/types.h>
3+
#include <stdlib.h>
4+
#include <stdio.h>
5+
#include <unistd.h>
6+
#include <errno.h>
7+
#include <string.h>
8+
#include <assert.h>
9+
#include <fcntl.h>
10+
#include <netinet/in.h>
11+
#include <arpa/inet.h>
12+
13+
int main(int argc, char* argv[]) {
14+
if(argc <= 2) {
15+
printf("Wrong number of parameters!");
16+
return 1;
17+
}
18+
19+
char* ip = argv[1];
20+
int port = atoi(argv[2]);
21+
22+
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
23+
assert(sockfd >= 0);
24+
25+
struct sockaddr_in address;
26+
memset(&address, 0, sizeof(address));
27+
address.sin_family = AF_INET;
28+
address.sin_port = htons(port);
29+
inet_pton(AF_INET, ip, &address.sin_addr);
30+
31+
int ret = bind(sockfd, (struct sockaddr*)&address, sizeof(address));
32+
assert(ret != -1);
33+
34+
ret = listen(sockfd, 5);
35+
assert(ret != -1);
36+
37+
struct sockaddr_in client;
38+
socklen_t client_length = sizeof(client);
39+
int connfd = accept(sockfd, (struct sockaddr*)&client, &client_length);
40+
if(connfd < 0) {
41+
printf("error\n");
42+
return 1;
43+
} else {
44+
45+
// 用管道连接起 socket的输入和输出 实现0拷贝
46+
47+
int pipefd[2];
48+
assert(ret != -1);
49+
ret = pipe(pipefd);
50+
ret = splice(connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
51+
assert(ret != -1);
52+
ret = splice(pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
53+
assert(ret != -1);
54+
close(connfd);
55+
}
56+
57+
close(sockfd);
58+
return 0;
59+
}

README.md

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ title: Linux高性能服务器编程记录
99
每学习一部分就写一个demo
1010

1111
# demo 记录
12-
12+
```
1313
2019年8月20日 编写demo <1. 客户端自动发送固定信息> -- 所用基础连接部分和TCP连接部分
14-
1514
2019年8月22日 编写demo <2. 伪全双工TCP> -- 所用截止到网络Api之前
16-
1715
2019年8月22日 编写demo <3. 伪全双工UDP> -- 所用截止到网络Api之前
18-
1916
2019年8月25日 复现 源码 <4. 代码清单5-12 访问daytime服务> -- 第五章结束
20-
17+
2019年8月25日 复现 源码 <5. 代码清单6-3 用sendfile函数传输文件>
18+
2019年8月25日 复现 源码 <6. 代码清单6-4 用splice函数实现的回射服务器>
19+
```
2120
## 客户端自动发送固定信息
2221

2322
## 伪全双工TCP
@@ -380,4 +379,132 @@ int getnameinfo(const struct sockaddr* sockaddr, socklen_t addrlen, char* host,
380379
telnet ip port #来连接服务器的此端口
381380
netstat -nt | grep port #来查看此端口的监听
382381
```
382+
## 第六章高级IO函数
383+
384+
Linux提供的高级IO函数, 自然是特定条件下能力更强, 不然要他干啥, 特定条件自然限制了他的使用频率
385+
386+
### 创建文件描述符
387+
#### pipe函数
388+
这个函数可用于创建一个管道, 实现进程间的通信. (后面13.4节介绍更多, 待我学到后回来补坑)
389+
390+
```c
391+
// 函数定义
392+
// 参数文件描述符数组 fd[0] 读入 fd[1]写入 单向管道
393+
// 成功返回0, 并将一对打开的文件描述符填入其参数指向的数组
394+
// 失败返回-1 errno
395+
#include <unistd.h>
396+
int pipe(int fd[2]);
383397

398+
// 双向管道
399+
// 第一个参数为 协议PF_UNIX(书上是AF_UNIX, 还需要实际测试下)
400+
#include <sys/types.h>
401+
#include <sys/socket.h>
402+
int socketpair(int domain, int type, int protocol, int fd[2]);
403+
```
404+
#### dup和dup2函数
405+
**文件描述符**
406+
```
407+
文件描述符在是一个非负整数。是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。
408+
STDOUT_FILENO(值为1)- 值为1的文件描述符为标准输出, 关闭STDOUT_FILENO后用dup即可返回最小可用值(目前为, 1) 这样输出就重定向到了调用dup的参数指向的文件
409+
```
410+
411+
将标准输入重定向到一个`文件`或一个网络连接
412+
```c
413+
#include <unistd.h>
414+
// 返回的文件描述符总是取系统当前可用的最小整数值
415+
int dup(int file_descriptor);
416+
// 返回的整数值不小于file_descriptor_two参数.
417+
int dup2(int file_descriptor, int file_descriptor_two);
418+
```
419+
dup函数创建一个新的文件描述符, 新的文件描述符和原有的file_descriptor共同指向相同的目标.
420+
421+
### 读写数据
422+
#### readv/writev
423+
```c
424+
#include <sys/uio.h>
425+
// count 为 vector的长度, 即为有多少块内存
426+
// 成功时返回写入\读取的长度 失败返回-1
427+
ssize_t readv(int fd, const struct iovec* vector, int count);
428+
ssize_t writev(int fd, const struct iovec* vector, int count);
429+
430+
struct iovec {
431+
void* iov_base /* 内存起始地址*/
432+
size_t iov_len /* 这块内存长度*/
433+
}
434+
```
435+
sendfile函数
436+
```c
437+
#include <sys/sendfile.h>
438+
// offset为指定输入流从哪里开始读, 如果为NULL 则从开头读取
439+
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
440+
441+
O_RDONLY只读模式
442+
O_WRONLY只写模式
443+
O_RDWR读写模式
444+
int open(file_name, flag);
445+
```
446+
stat结构体, 可用fstat生成, **简直就是文件的身份证**
447+
```c
448+
#include <sys/stat.h>
449+
struct stat
450+
{
451+
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
452+
ino_t st_ino; /* inode number -inode节点号*/
453+
mode_t st_mode; /* protection -保护模式?*/
454+
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
455+
uid_t st_uid; /* user ID of owner -user id*/
456+
gid_t st_gid; /* group ID of owner - group id*/
457+
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
458+
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
459+
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
460+
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
461+
time_t st_atime; /* time of last access -最近存取时间*/
462+
time_t st_mtime; /* time of last modification -最近修改时间*/
463+
time_t st_ctime; /* time of last status change - */
464+
};
465+
```
466+
**身份证**生成函数
467+
```c
468+
// 第一个参数需要调用open生成文件描述符
469+
// 下面其他两个为文件全路径
470+
int fstat(int filedes, struct stat *buf);
471+
472+
int stat(const char *path, struct stat *buf);
473+
// 当路径指向为符号链接的时候, lstat为符号链接的信息. 二stat为符号链接指向文件信息
474+
int lstat(const char *path, struct stat *buf);
475+
476+
/*
477+
* ls -s source dist 建立软连接, 类似快捷方式, 也叫符号链接
478+
* ls source dist 建立硬链接, 同一个文件使用多个不同的别名, 指向同一个文件数据块, 只要硬链接不被完全
479+
* 删除就可以正常访问
480+
* 文件数据块 - 文件的真正数据是一个文件数据块, 打开的`文件`指向这个数据块, 就是说
481+
* `文件`本身就类似快捷方式, 指向文件存在的区域.
482+
*/
483+
```
484+
#### mmap和munmap函数
485+
486+
一个创建一块进程通讯共享的内存(可以将文件映射入其中), 一个释放这块内存
487+
```c
488+
#include <sys/mman.h>
489+
490+
// start 内存起始位置, 如果为NULL则系统分配一个地址 length为长度
491+
// port参数 PROT_READ(可读) PROT_WRITE(可写) PROT_EXEC(可执行), PROT_NONE(不可访问)
492+
// flag参数 内存被修改后的行为
493+
// - MAP_SHARED 进程间共享内存, 对内存的修改反映到映射文件中
494+
// - MAP_PRIVATE 为调用进程私有, 对该内存段的修改不会反映到文件中
495+
// - MAP_ANONUMOUS 不是从文件映射而来, 内容被初始化为0, 最后两个参数被忽略
496+
// 成功返回区域指针, 失败返回 -1
497+
void* mmap(void* start, size_t length, int port, int flags, int fd, off_t offset);
498+
// 成功返回0 失败返回-1
499+
int munmap(void* start, size_t length);
500+
```
501+
#### splice函数
502+
用于在两个文件名描述符之间移动数据, 0拷贝操作
503+
```c
504+
#include <fcntl.h>
505+
// fd_in 为文件描述符, 如果为管道文件描述符则 off_in必须为NULL, 否则为读取开始偏移位置
506+
// len为指定移动的数据长度, flags参数控制数据如何移动.
507+
// - SPLICE_F_NONBLOCK 非阻塞splice操作, 但会受文件描述符自身的阻塞
508+
// - SPLICE_F_MORE 给内核一个提示, 后续的splice调用将读取更多的数据???????
509+
ssize_t splice(int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags);
510+
```

0 commit comments

Comments
 (0)