Linux 高并发服务器开发
Linux 系统编程入门
GCC
![image-20230411142304527](/2023/04/11/linux/Gcc.png)
![image-20230411142953910](/2023/04/11/linux/gcc2.png)
静态库
库文件用于保存代码
1.不能单独运行
2.代码保密性好,方便部署与分发
静态库是指在编译时将库的代码和数据编译成可执行程序中的一个单独的可执行模块。这意味着静态库的代码在编译时被复制到可执行程序中,因此它们不需要在运行时加载。静态库的好处是可以获得更快的执行速度,因为代码和数据已经在可执行程序中,而不需要在运行时进行加载和链接。但是静态库的缺点是它会使得可执行程序的大小变得更大,并且在多个可执行程序之间共享代码会变得更加困难。
动态库是在运行时加载的库,它们不像静态库那样被编译到可执行程序中。当可执行程序运行时,它会通过动态链接器加载动态库并将其链接到可执行程序中。这使得动态库可以被多个可执行程序共享,从而节省磁盘空间和内存。另外,动态库可以被更新和替换而不需要重新编译可执行程序,因此它们更加灵活。但是,动态库的缺点是在加载和链接时需要一定的时间,因此可能会导致一定的性能损失。
命名规则:
Linux : libxxx.a
Windows: libxxx.lib
静态库制作
1.用Gcc 获得.o文件
2.将.o文件打包
1 2 3 4
| ar rcs libxxx.a xxx.o r-将文件存入备存文件 c-建立备存文件 s-索引
|
其中 xxx
为库名
静态库使用
![image-20230411154022370](/2023/04/11/linux/tree.png)
1
| gcc main.c -o app -I ./include/ -L ./lib/ -l suanshu
|
-I
指定include包含文件的搜索目录
-L
指定编译时候,搜索的库目录
-l
指定使用的库名
动态库
动态库文件命名规则:
Linux : libxxx.so
Windows: libxxx.dll
动态库制作
1.gcc 得到.o 文件 得到和位置无关的代码 gcc -c -fpic/-fPIC a.c b.c
2.gcc 得到动态库 gcc shared a.o b.o -o libxxx.so
使用
![image-20230411162220648](/2023/04/11/linux/%E5%8A%A8%E6%80%81%E5%BA%93tree.png)
gcc main.c -o app -I ./include/ -L ./lib/ -l calc
类似于静态库
由于动态库的代码不会被打包到可执行程序中
所以在运行用到了动态库的程序之前要将 动态库加载到内存中
用ldd
来检查可执行程序的动态库链接情况
![](/2023/04/11/linux/%E5%8A%A8%E6%80%81%E5%BA%93%E5%8A%A0%E8%BD%BD%E5%8F%AF%E6%89%A7%E8%A1%8C%E4%BB%A3%E7%A0%81.png)
写完动态库之后需要改变环境变量 或者 /etc/ld.so.cache 文件 来使程序能使用动态库
LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库的绝对路径
ps:这种方法并不用会把动态库放到/usr/bin/中,重新打开一个终端的话,需要重新将库路径添加到环境变量 LD_LIBRARY_PATH 中,是临时的
修改 .bashrc
在home 目录下 用vim 打开 .bashrc 在最后插入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库的绝对路径
修改完之后 . .bashrc
更新环境
此方法是用户级别的
修改 /etc/profile
sudo vim /etc/profile
同样在最下面 插入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库的绝对路径
之后 source /etc/profile
更新即可
/etc/ld/so.cache
因为是二进制文件所有需要间接修改此文件
打开 vim /etc/ld.so.conf
在最后直接添加 动态库的绝对路径即可
/lib/ /usr/lib
将动态库移动在这两个目录下即可,但是不推荐,因为有可能覆盖原有的文件
动态库和静态库对比
编译过程
![](/2023/04/11/linux/%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B.png)
![](/2023/04/11/linux/%E9%9D%99%E6%80%81%E5%BA%93.png)
![linux](/2023/04/11/linux/%E5%8A%A8%E6%80%81%E5%BA%93%E4%BC%98%E7%BC%BA%E7%82%B9.png)
Makefile
IO函数
![](/WindowsIO.png)
文件描述符
缓冲区地址
读写指针
虚拟内存地址
![image-20230416124327957](/2023/04/11/linux/%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98.png)
实际上并不存在,生命周期等同程序生命周期
Linux网络编程
socket
小端字节序
字节序分为大端字节序(Big-Endian) 和小端字节序(Little-Endian)。大端字节序是指一个整数的最高位字节(23 ~ 31 bit)存储在内存的低地址处,低位字节(0 ~ 7 bit)存储在内存的高地址处;小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。
0x 01 02 03 04 - ff = 255
内存的方向 —–> 内存的低位 —–> 内存的高位
以 0x 11 22 33 44 12 34 56 78 为例子
![image-20230421143355158](/2023/04/11/linux/%E5%B0%8F%E7%AB%AF%E5%AD%97%E8%8A%82%E5%BA%8F.png)
大端字节序
0x 01 02 03 04
内存的方向 —–> 内存的低位 —–> 内存的高位
0x 12 34 56 78 11 22 33 44 为例子
![image-20230421143416999](/2023/04/11/linux/%E5%A4%A7%E7%AB%AF%E5%AD%97%E8%8A%82%E5%BA%8F.png)
TCP多进程实现并发服务器
服务端
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
|
void recyleChild(int arg) { while(1) { int ret = waitpid(-1, NULL, WNOHANG); if(ret == -1) { // 所有的子进程都回收了 break; }else if(ret == 0) { // 还有子进程活着 break; } else if(ret > 0){ // 被回收了 printf("子进程 %d 被回收了\n", ret); } } }
int main() {
struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = recyleChild; // 注册信号捕捉 sigaction(SIGCHLD, &act, NULL);
// 创建socket int lfd = socket(PF_INET, SOCK_STREAM, 0); if(lfd == -1){ perror("socket"); exit(-1); }
struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(9999); saddr.sin_addr.s_addr = INADDR_ANY;
// 绑定 int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr)); if(ret == -1) { perror("bind"); exit(-1); }
// 监听 ret = listen(lfd, 128); if(ret == -1) { perror("listen"); exit(-1); }
// 不断循环等待客户端连接 while(1) {
struct sockaddr_in cliaddr; int len = sizeof(cliaddr); // 接受连接 int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len); if(cfd == -1) { if(errno == EINTR) { continue; } perror("accept"); exit(-1); }
// 每一个连接进来,创建一个子进程跟客户端通信 pid_t pid = fork(); if(pid == 0) { // 子进程 // 获取客户端的信息 char cliIp[16]; inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp)); unsigned short cliPort = ntohs(cliaddr.sin_port); printf("client ip is : %s, prot is %d\n", cliIp, cliPort);
// 接收客户端发来的数据 char recvBuf[1024]; while(1) { int len = read(cfd, &recvBuf, sizeof(recvBuf));
if(len == -1) { perror("read"); exit(-1); }else if(len > 0) { printf("recv client : %s\n", recvBuf); } else if(len == 0) { printf("client closed....\n"); break; } write(cfd, recvBuf, strlen(recvBuf) + 1); } close(cfd); exit(0); // 退出当前子进程 }
} close(lfd); return 0; }
|
客户端
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
| // TCP通信的客户端
int main() {
// 1.创建套接字 int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd == -1) { perror("socket"); exit(-1); }
// 2.连接服务器端 struct sockaddr_in serveraddr; serveraddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr.s_addr); serveraddr.sin_port = htons(9999); int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret == -1) { perror("connect"); exit(-1); } // 3. 通信 char recvBuf[1024]; int i = 0; while(1) { sprintf(recvBuf, "data : %d\n", i++); // 给服务器端发送数据 write(fd, recvBuf, strlen(recvBuf)+1);
int len = read(fd, recvBuf, sizeof(recvBuf)); if(len == -1) { perror("read"); exit(-1); } else if(len > 0) { printf("recv server : %s\n", recvBuf); } else if(len == 0) { // 表示服务器端断开连接 printf("server closed..."); break; }
sleep(1); }
// 关闭连接 close(fd);
return 0; }
|
UDP通信
服务端
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
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h>
int main() {
int fd = socket(PF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket"); exit(-1); }
struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if(ret == -1) { perror("bind"); exit(-1); }
while(1) { char recvbuf[128]; char ipbuf[16];
struct sockaddr_in cliaddr; int len = sizeof(cliaddr);
int num = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&cliaddr, &len);
printf("client IP : %s, Port : %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), ntohs(cliaddr.sin_port));
printf("client say : %s\n", recvbuf);
sendto(fd, recvbuf, strlen(recvbuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
}
close(fd); return 0; }
|
客户端
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
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h>
int main() {
int fd = socket(PF_INET, SOCK_DGRAM, 0); if(fd == -1) { perror("socket"); exit(-1); }
struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(9999); inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);
int num = 0; while(1) {
char sendBuf[128]; sprintf(sendBuf, "hello , i am client %d \n", num++); sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&saddr, sizeof(saddr));
int num = recvfrom(fd, sendBuf, sizeof(sendBuf), 0, NULL, NULL); printf("server say : %s\n", sendBuf);
sleep(1); }
close(fd); return 0; }
|