input:从磁盘读取数据到内存中
output: 把内存中的数据写入到磁盘上
常规文件 - 1.c 1.txt 1.sh 1.cpp a.out
目录文件 d day1
字符设备 c 键盘鼠标摄像头
块设备 b 磁盘
管道文件 p pipe
套接字文件 s 网络通信的通道
符号链接文件 l 类似于windows快捷方式
1、文件IO------(linux系统提供) 系统调用:当我们的应用程序要使用一些底层的功能的时候,不应该自行访问底层,而应该向操作系统发出请求。
特点:
不带缓冲区
操作系统直接提供的函数接口
调用系统调用是很耗费资源的
2、标准IO------(C库提供)
C库函数:在系统调用接口之上封装的接口,一个C库函数可以封装多个系统调用函数。
作用:
增强了代码的可移植性,复用性
提高了效率。 标准IO增加了一个【缓冲机制】
FILE *fopen(const char *pathname, const char *mode);
功能:打开文件
参数:
pathname: 文件的路径名
mode: 打开文件的方式
r: 以只读的方式打开文件,文件已存在
r+ : 以读写的方式打开文件,文件已存在
w: 以可写的方式打开文件,文件已存在,则清空文件的内容
文件不存在,则创建一个文件
w+: 以读写的方式打开文件,文件已存在,则清空文件的内容
文件不存在,则创建一个文件
a: 以可写的方式打开文件,文件存在,追加内容在末尾
a+: 以读写的方式打开文件,文件存在,追加内容在末尾
返回值:
成功: FILE * 流指针 本质上是一个结构体指针
失败: NULL
FILE:系统会自动为使用的文件在内存中开辟一片空间,来存储该文件的详细信息,这个空间类型为 FILE 结构体类型,该结构体由系统设计。
FILE *:流指针,在标准IO中,每次成功打开一个文件,都会返回一个流指针,这个流指针就描述了一个文件,所有的标准IO都围绕流指针来进行。
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp;
int count = 0;
while(1)
{
fp= fopen("2.txt","w");
if(fp == NULL){
//printf("%s\n", strerror(errno));
perror("fopen 2.txt");
//return -1;
break;
}
count++;
printf("fopen success!\n");
}
printf("count = %d\n", count);
//fclose(fp);
return 0;
}
int fclose(FILE *stream);
功能: 关闭流指针
参数: 流指针
为什么要关闭一个文件?
一、防止其他进程操作这个文件
二、释放结构体占用的资源
在程序结束时,系统自动回收资源(不完全),所以尽量写上fclose。
int fgetc(FILE *stream);
注意:每次调用fgetc,它的文件指针会偏移
功能: 读取一个字节的数据
参数: 流指针
返回值:
成功:ASCII码值
失败:-1
注意:1、fgetc的返回值类型是int!!! ,如果系统不一样定义成char可能会有问题
2、fgetc函数每行字符结束都会加'\n'
代码:
结果:
转载:fgetc踩过的坑https://blog.csdn.net/modi000/article/details/105839310?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169251678316800197062649%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169251678316800197062649&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-105839310-null-null.142%5Ev93%5Einsert_down28v1&utm_term=fgetc%E5%9D%91&spm=1018.2226.3001.4187
int fputc(int c, FILE *stream);
注意:每次调用fputc,它的文件指针会偏移
功能:写入一个字节的数据
参数:
c: 一个字节
stream:流指针
返回值:
成功: ASCII码值
失败: -1
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("a.txt", "w+");
if(fp == NULL)
{
perror("fopen a.txt");
return -1;
}
fputc(97, fp);
fputc('a', fp);
fputc(0141, fp);
fputc(0x61, fp);
fputc(98, fp);
fputc('\n', fp);
rewind(fp); //把文件指针移到文件的开头
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
fclose(fp);
return 0;
}
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("a.txt", "r+");
if(fp == NULL)
{
perror("fopen a.txt");
return -1;
}
printf("%d\n", fgetc(fp));
printf("%d\n", fgetc(fp));
fputc('c', fp);
fputc('d', fp);
fclose(fp);
return 0;
}
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("a.txt", "r");
if(fp == NULL)
{
perror("fopen a.txt");
return -1;
}
int n1 = fputc('1', fp);
int n2 = fputc('2', fp);
printf("n1=%d n2=%d\n", n1,n2);
fclose(fp);
return 0;
}
char *fgets(char *s, int size, FILE *stream);
注意:每次调用fgets,它的文件指针会偏移
功能:读取一行的数据,存到s中
参数:
s:代表要保存到的内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名 char s[];
size: 读取的字节数 size-1,因为fgets会在字符串后加'\0',所以要-1
stream: 流指针
返回值:
成功:字符串首元素地址
失败:NULL
注意:这里的一行,是指 size-1 或者 '\n'
读到文件的末尾,返回NULL
#include
#include
#include
int main(int argc, const char *argv[])
{
char buf[100] = {0};
char *p = fgets(buf, 10, stdin);
printf("%s\n", p);
return 0;
}
#include
#include
#include
int main(int argc, const char *argv[])
{
char buf[10] = {0};
fgets(buf, 10, stdin);
//去除换行
buf[strlen(buf)-1] = '\0';
printf("%s", buf);
return 0;
}
int fputs(const char *s, FILE *stream);
注意:每次调用fputs,它的文件指针会偏移
功能:把内存中的数据写入到流指针指向的文件中
参数:
s :内存地址 char s[];
stream: 流指针
返回值:
成功:非负值
失败:-1
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("4.txt", "w+");
char buf[100] = "hello world\n";
while(1)
{
fgets(buf, 100, stdin);
buf[strlen(buf)-1] = '\0';
if(strcmp(buf, "quit") == 0 )
{
break;
}
fputs(buf, fp);
memset(buf, 0 , 100);
}
fclose(fp);
return 0;
}
void rewind(FILE *stream) ;
功能:将文件指针移动到文件开头
参数:流指针
int fseek(FILE *stream, long offset, int whence);
功能:定位文件指针
参数:
stream: 流指针
offset: 偏移量
+100 :向后偏移100个字节
-100 :向前偏移100个字节
whence: 基点
SEEK_SET 文件开头
SEEK_END 文件末尾
SEEK_CUR 文件当前的位置
返回值:
成功:0
失败:-1
定位到文件末尾: fseek( fp , 0 , SEEK_END );
定位到文件末尾的前一个字节:fseek( fp , -1 , SEEK_END );
int main(int argc, const char *argv[])
{
FILE *fp = fopen("aa.txt", "w+");
fputs("hello world\n", fp);
fseek(fp, -6, SEEK_END);
fputs("xxx", fp);
fputc('\0', fp);
fseek(fp, 0, SEEK_SET);
char buf[100] = {0};
fgets(buf, 100, fp);
printf("%s\n", buf);
fclose(fp);
return 0;
}
long ftell(FILE *stream);
功能: 返回文件指针到文件开头的长度 (获取文件大小)
参数:流指针
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("1.txt", "r+");
fputs("hello world\n", fp);
long int n = ftell(fp);
printf("%ld\n", n);
fseek(fp, -7 , SEEK_CUR);
n = ftell(fp);
printf("%ld\n", n);
fclose(fp);
return 0;
}
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能: 读取数据
参数:
ptr:内存地址 char buf[];
size: 每块的字节数
nmemb:块数
stream:流指针
返回值:
成功:块数
失败:-1
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp = fopen("1.txt", "rb");
if(fp == NULL)
{
perror("fopen 1.txt");
return -1;
}
char buf[1000] = {0};
int m = fread(buf, 1000, 1, fp);
printf("m=%d\nbuf=%s\n", m, buf);
fclose(fp);
return 0;
}
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能: 写入数据
参数:
ptr:内存地址
size: 每块的字节数
nmemb:块数
stream:流指针
返回值:
成功:块数
失败:-1
struct AA{
int a;
float b;
char c;
};
int main(int argc, const char *argv[])
{
struct AA t = {100, 3.14 , 'b'};
struct AA n;
FILE *fp = fopen("a.txt", "wb+");
int m = fwrite( &t, sizeof(struct AA), 1, fp);
printf("m=%d\n", m);
rewind(fp);
fread( &n, sizeof(struct AA), 1, fp);
printf("%d %.2f %c\n", n.a, n.b, n.c);
fclose(fp);
return 0;
}
分为三种:行缓存、无缓存、全缓存
1、行缓存:和终端相关的缓冲区就是行缓存。典型代表:printf、stdin、stdout
2、无缓存:没有缓冲区。典型代表:stderr
3、全缓存:和文件相关的就是全缓存,fopen打开的文件。典型代表是对磁盘文件的读写
1、行缓存:大小1024
#include
int main(int argc, const char* argv[])
{
int ch;
对于标准输入行缓存大小验证的时候,必须有输入才有数据
scanf("%d", &ch);
printf("size = %ld\n", stdin->_IO_buf_end - stdin->_IO_read_base);
1024
printf("size = %ld\n", stdout->_IO_buf_end - stdout->_IO_read_base);
1024
fprintf(stderr,"q121231qweqweqweqweqweqweqwe2");
printf("size = %ld\n", stderr->_IO_buf_end - stderr->_IO_read_base);
0
while(1);
return 0;
}
2、全缓存:大小4096
#include
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./tim.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
fprintf(fp,"q121231qweqweqweqweqweqweqwe2");
printf("size = %ld\n",fp->_IO_buf_end - fp->_IO_buf_base);
//4096
return 0;
}
3、无缓存:为0
1.行缓存满了或遇到'\n'输出条件,可刷新行缓存
2.fflush可以强制刷新 (fflush)
3.文件关闭的时候 fclose(stdout)
4.程序结束的时候exit或return
int fprintf(FILE *stream, const char *format, ...);
注意缓存区问题
功能: 按照一定的格式组装数据,把数据放在流指针
参数:
stream:流指针
format:格式控制符
... :不定参数
返回值:
成功:字符串的长度
失败:-1
int main(int argc, const char *argv[])
{
FILE *fp = fopen("2.txt", "w");
int m = fprintf(fp, "%d-%d-%d %d:%d:%d", 2023,8,15,11,33,30);
printf("m=%d\n", m);
int a = 10;
char b = 'b';
float c = 3.14;
fprintf( stdout , "%d***%c***%.2f\n" , a, b, c );
fclose(fp);
return 0;
}
int fscanf(FILE *stream, const char *format, ...);
功能: 按照一定的格式提取数据,从流指针中提取数据到不定参数中(可用于提取值)
参数:
stream:流指针
format:格式控制符
... :不定参数
返回值:
成功:不定参数的个数
失败:-1
int main(int argc, const char *argv[])
{
FILE *fp = fopen("./2.txt","w+");
//fprintf(fp, "%d*%d",10,20);
fputs("10*20", fp);
rewind(fp);
int a,b,c,d,e,f;
int m = fscanf(fp, "%d*%d", &a, &b);
printf("m = %d\n%d %d\n", m,a, b);
fclose(fp);
return 0;
}
int sprintf(char *str, const char *format, ...);
注意缓存区问题
功能: 按照一定的格式组装数据,把数据放在内存地址
参数:
str: 内存地址 char str[];
format:格式控制符
... :不定参数
返回值:
成功:字符串的长度
失败:-1
#include
#include
#include
int main(int argc, const char *argv[])
{
char buf[100] = {0};
int m = sprintf(buf, " %d-%d-%d %d:%d:%d %.2f#%c#%c*%s \n", 2023, 8,15,11,45,40
, 3.14 , 'a', 'B', "hello");
printf("m=%d\n", m);
printf("%s", buf);
return 0;
}
int sscanf(const char *str, const char *format, ...);
功能: 按照一定的格式提取数据,从内存地址中提取数据到不定参数中
参数:
str:内存地址
format:格式控制符
... :不定参数
返回值:
成功:不定参数的个数
失败:-1
int main(int argc, const char *argv[])
{
char buf[100] = "97*98*99";
int a,b,c;
sscanf(buf, "%d*%d*%d", &a, &b, &c);
printf("%c %c %c\n", a,b , c);
printf("%d %d %d\n", a,b , c);
printf("%#x %#x %#x\n", a,b,c);
return 0;
}
#include
time_t t;
time_t time(time_t *tloc);
功能: 从1970-1-1 0:0:0 到现在的秒数
time(&t); // time(NULL);
int main(int argc, const char *argv[])
{
time_t t;
while(1)
{
//printf("%ld\n", time(&t));
printf("%ld\n", time(NULL));
sleep(1);
}
return 0;
}
#include
time_t t;
struct tm *localtime(const time_t *timep);
功能: 把秒数转化成日历
参数:
timep:&t
返回值:
成功:struct tm*
失败:NULL
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
};
int main(int argc, const char *argv[])
{
time_t t;
FILE *fp = fopen("time.txt", "a");
while(1)
{
//printf("%ld\n", time(&t));
//printf("%ld\n", time(NULL));
time(&t);
struct tm *p = localtime(&t);
printf("%d-%d-%d %d:%d:%d\n",
p->tm_year+1900,
p->tm_mon+1,
p->tm_mday,
p->tm_hour,
p->tm_min,
p->tm_sec);
fprintf(fp ,"%d-%d-%d %d:%d:%d\n",
p->tm_year+1900,
p->tm_mon+1,
p->tm_mday,
p->tm_hour,
p->tm_min,
p->tm_sec);
fflush(fp);
sleep(1);
}
fclose(fp);
return 0;
}
char *ctime(const time_t *timep);
功能: 把秒数转化成日历
参数:
timep:&t
返回值:
成功:字符串(日历)
失败:NULL
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
time_t t;
while(1)
{
//printf("%ld\n", time(&t));
//printf("%ld\n", time(NULL));
time(&t);
char *p = ctime(&t);
printf("%s\n", p);
sleep(1);
}
return 0;
}
文件描述符实际上是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符,进程使用它来标识打开的文件。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
Linux系统为程序中每个打开的文件都分配一个文件描述符,文件IO操作通过文件描述符来完成。
文件描述符在形式上是一个顺序分配的非负整数。从0开始分配,依次递增。比如 0,1,2表示 stdin stdout stderr,一般最大打开的文件描述符数量为1024(0~1023)
#include
#include
#includeint open(const char *pathname, int flags);
功能:打开已经存在的文件int open(const char *pathname, int flags, mode_t mode);
功能:创建文件 (文件是不存在的)
参数:
pathname:文件的路径名
flags: 打开方式
O_RDONLY 只读
O_WRONLY 可写
O_CREAT 创建
O_RDWR 读写
O_TRUNC 清空
O_APPEND 追加
mode: 创建文件的权限 rw-rw-rw- 0666
返回值:
成功:文件描述符 一个非负整数 从0开始 范围0~1023
每打开一个文件,就会有一个对应文件描述符产生
失败:-1
实际创建的文件权限需要经过一个公式计算得到: mode & (~umask) 注:umask=002
标准IO 文件IO
r O_RDONLY
r+ O_RDWR
w O_WRONLY | O_CREAT | O_TRUNC
w+ O_RDWR | O_CREAT | O_TRUNC
a O_WRONLY | O_CREAT | O_APPEND
a+ O_RDWR | O_CREAT | O_APPEND
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = open("./b.txt", O_CREAT | O_RDWR, 0666);
if(fd < 0)
{
perror("open");
return -1;
}
printf("open success!\n");
printf("fd = %d\n", fd); //3
close(fd);
return 0;
}
#include
int close(int fd);
功能: 关闭文件描述符
参数: 文件描述符
#include
ssize_t read(int fd, void *buf, size_t count);
功能:从文件中读取数据到内存
参数:
fd: 文件描述符
buf: 内存地址
count : 读取的字节数
返回值:
成功:
> 0 实际读取的字节数
== 0 读到文件末尾了
失败:
< 0
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = open("./1.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
printf("open success!\n");
printf("fd = %d\n", fd); //3
//read
char buf[100] = {0};
int num = 0;
while(1)
{
int n = read(fd, buf, 99);
if(n < 0)
{
perror("read");
break;
}
else if(n == 0) //说明读到文件的末尾了
{
break;
}
num += n;
printf("n=%d\nbuf=%s\n", n, buf);
memset(buf, 0 ,100); //bzero(buf,100);
}
printf("num = %d\n", num);
close(fd);
return 0;
}
#include
ssize_t write(int fd, const void *buf, size_t count);
功能:把内存中的数据写入到文件中
参数:
fd: 文件描述符
buf: 内存地址
count : 写入的字节数
返回值:
成功:
>= 0 实际写入的字节数
失败:
< 0
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = open("./2.txt", O_RDWR|O_TRUNC);
if(fd < 0)
{
perror("open");
return -1;
}
printf("open success!\n");
printf("fd = %d\n", fd); //3
//write
int m = write(fd, "hello world\n", 12);
printf("m=%d\n", m);
close(fd);
return 0;
}
#include
#includeoff_t lseek(int fd, off_t offset, int whence);
功能:定位文件指针
参数:
fd: 文件描述符
offset: 偏移量
+100 :向后偏移100个字节
-100 :向前偏移100个字节
whence: 基点
SEEK_SET 文件开头
SEEK_END 文件末尾
SEEK_CUR 文件当前的位置
返回值:
成功:0
失败:-1
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = open("./test.txt", O_CREAT| O_RDWR|O_TRUNC, 0666);
if(fd < 0)
{
perror("open");
return -1;
}
printf("open success!\n");
printf("fd = %d\n", fd); //3
//LSEEK
write( fd, "hello", 5);
lseek(fd, 100, SEEK_END);
write(fd, "world", 5);
close(fd);
return 0;
}
#include
#includeDIR *opendir(const char *name);
功能:打开目录
参数:
name:目录名
返回值:
成功: DIR *
失败: NULL
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
//1.opendir
if(argc != 2)
{
printf("%s dir\n", argv[0]);
return -1;
}
DIR *dir = opendir( argv[1] );
if(dir == NULL)
{
perror("opendir");
return -1;
}
while(1)
{
struct dirent *p = readdir(dir);
if(p == NULL)
{
break;
}
if( strncmp(p->d_name, ".",1) == 0 )
{
continue;
}
printf("%s\n", p->d_name);
}
closedir(dir);
return 0;
}
#include
#includestruct dirent *readdir(DIR *dirp);
功能:读取目录中的信息
参数:
dirp: opendir 的返回值
返回值:
成功: struct dirent *
失败: NULL (一直遍历目录,直到遍历完成,返回NULL)
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
#include
#includeint closedir(DIR *dirp);
功能:关闭目录
参数:
dirp: opendir 的返回值
#include
#include
#includestruct stat mybuf;
int stat(const char *path, struct stat *buf);
功能:得到文件的属性
参数:
path:文件路径名
buf: &mybuf
-rw-rw-r-- 1 farsight farsight 593 4月 19 09:26 01picture.c
-rw-rw-r-- 1 farsight farsight 537 4月 19 09:53 02readdir.c
-rw-rw-r-- 1 farsight farsight 161 4月 19 10:23 03stat.c
-rwxrw-rw- 1 farsight farsight 97893 4月 19 09:26 5.jfif
-rwxrwxr-x 1 farsight farsight 7357 4月 19 09:53 a.out
//查看设备号
major minor
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; 文件的权限、文件的类型 /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; 所属用户ID /* user ID of owner */
gid_t st_gid; 所属组ID /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; 文件的大小 /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; 最后一次访问时间 /* time of last access */
time_t st_mtime; 最后一次修改时间 /* time of last modification */
time_t st_ctime; 最后一次文件文件属性修改时间 /* time of last status change */
};
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
struct stat mybuf;
stat("./hk/", &mybuf);
//类型
if(S_ISREG(mybuf.st_mode))
printf("- 常规文件\n");
else if(S_ISDIR(mybuf.st_mode))
printf("d 目录文件\n");
//权限
if( mybuf.st_mode & (0x1<<0) )
printf("other: x\n");
else
printf("other: -\n");
if( mybuf.st_mode & (0x1<<1) )
printf("other: w\n");
else
printf("other: -\n");
if( mybuf.st_mode & (0x1<<2) )
printf("other: r\n");
else
printf("other: -\n");
//硬链接
printf("link = %ld\n", mybuf.st_nlink);
//组名
struct passwd *p = getpwuid(mybuf.st_uid);
printf("username = %s\n", p->pw_name);
//size
printf("size=%ld\n", mybuf.st_size);
//time
struct tm *k = localtime(&mybuf.st_mtime);
printf("%d-%d %d:%d\n", k->tm_year+1900,
k->tm_mon+1,
k->tm_mday,
k->tm_hour);
return 0;
}
struct passwd *getpwuid(uid_t uid);
获取用户名
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
struct group *getgrgid(gid_t gid);
获取组名
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
示例:stat.c
文件类型
{
常规文件:S_ISREG '-'
目录:S_ISDIR 'd'
字符设备:S_ISCHR 'c'
块设备:S_ISBLK 'b'
管道:S_ISFIFO 'p'
套接字:S_ISSOCK 's'
符号链接:S_ISLNK 'l'
}
什么是进程?
是正在运行的程序的实例。当程序被操作系统加载到内存中并执行时,就产生了一个进程。一个进程可以包含多个线程,各自执行不同的任务。进程之间通常是相互独立的,无法直接访问对方的内存空间。通常一个进程一定程度上可以看成一个程序。
进程的概念
1、从用户的角度来说,进程就是程序的一次动态执行过程。
2、从操作系统角度:操作系统分配内存,CPU时间等系统资源的基本单位。
特点
1、每一个进程都有自己独立的虚拟地址空间和进程状态。
2、进程是分配资源的最小单位(基本单位)。
3、程序执行一次的过程,包含 创建,调度,消亡, 是动态的。
4、包括代码段,数据段,堆栈,PCB模块 task_struct(进程相关属性,时间片,进程id...)。
什么是线程?
是进程中的一个执行单元。一个进程可以包含多个线程,它们共享同一个地址空间和系统资源。线程之间的切换比进程之间的切换开销小,因此线程的创建和销毁比进程更加高效。
特点
1、存储在磁盘上的可执行的二进制编码, 是静态的
2、包括代码段,数据段
什么是程序?
是一系列指令的集合,用于完成特定任务。它是静态的,通常存储在磁盘或其他存储介质上,并不会直接执行。例如,一个文本编辑器的程序包括保存、打开、编辑等功能的指令集合。
进程和程序的区别和联系
联系
1、程序 = 文件(静态的可执行文件)
2、进程 = 执行中的程序 = 程序+执行状态
3、同一个程序的多次执行对应为不同的进程(ls的多次执行对应多个不同进程)
4、进程执行需要的资源:内存(保存代码和数据)+cpu(执行指令)
区别
1、程序是静态的,进程是动态的
(1)程序是有序代码的集合
(2)进程是程序的执行,进程有内核状态、用户态
2、进程相对于程序的生命周期是短暂的,程序是永久的
3、进程有PCB的数据结构
(1)程序 :数据段+代码段
(2)进程 :代码段+数据段+堆栈+PCB
4、一个进程只能对应一个程序,一个程序可以对应多个进程
执行态 R
停止态 T
等待态 S
僵尸态 Z
不可中断的等待 D
ps -aux / ps -ajx (静态查看进程)
top(动态查看进程)
kill(结束进程)
jobs(查看后台进程)
ctrl+z(让前台进程进入后台停止)
./a.out &(让程序在后台执行)
bg(后台执行)
fg(前台执行)
#include
#includepid_t fork(void);
功能:开辟一个子进程,子进程会复制父进程的所有用户空间内容(包括缓存)
返回值:成功返回0代表子进程,大于0(子进程的pid)是父进程,失败-1,并且子进程和父程互 不影响
一般用法
#include
#include
#include
#include
int main(int argc, char *argv[])
{
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
return -1;
}
else if(0 == pid)
{
while(1)
{
printf("child\n");
}
}
else
{
while(1)
{
printf("father\n");
}
}
return 0;
}
下面代码改一个'\n'为什么结果不一样?
不加'\n'打印4个hello,加了'\n'打印3个hello
#include
#include
#include
int main(int argc, char *argv[])
{
printf("hello"); //printf("hello\n");
fork();
printf("hello\n");
return 0;
}
原因:printf是行缓存。因为不加'\n'时,将hello存入缓存区,当fork时,连缓存区里的fork也一起拷贝了,所以打印了4个;加'\n'时,在父进程fork之前的hello直接输出在终端,当执行fork时已经没有了上一行的hello,所以是3个
pid_t vfork(void);
#include
#includepid_t getpid(void);
pid_t getppid(void);
功能:getpid()获取当前进程id,getppid()获取当前进程父亲id
返回值:getpid()返回当前进程id,getppid()返回当前进程父亲id
看下面代码,为什么不加sleep(1)后子进程的父id不等于父亲的id,加了sleep(1)后却又相等了?
#include
#include
#include
int main(int argc, char *argv[])
{
int a = 5;
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
return -1;
}
else if(0 == pid)
{
a = 6;
printf("child a=%d\n pid=%d ppid=%d", a, getpid(), getppid());
}
else
{
printf("father a=%d pid=%d ppid=%d cpid=%d\n", a, getpid(), getppid(),pid);
//sleep(1);
}
return 0;
}
不加sleep结果
加了sleep的结果
原因:不确定时父进程先执行,还是子进程先执行,但是加了sleep时,父进程就比子进程晚一些,所以子进程去找父进程id,找得到就返回真正的父进程id。没加sleep时,当父进程结束时,子进程去找父进程id,没有找到,只有系统随机分配给子进程id,所以这个被称为孤儿进程。
总结:父进程提前退出后,子进程被操作系统领养的一种情况,被操作系统领养的进程就被称为孤儿进程
看下面代码,有几个进程运行?
#include
#include
#include
int main(int argc, char *argv[])
{
fork()&&fork()||fork();
printf("hello\n");
return 0;
}
答案:有5个进程在执行
原因如下:
进程退出分为3种:正常退出(return)、exit函数退出、_exit函数退出
1、return退出
在main函数中使用return退出进程是我们常用的方法。
2、exit退出
使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作:
3、_exit退出
使用_exit函数退出进程的方法我们并不经常使用,_exit函数也可以在代码中的任何地方退出进程,但是_exit函数会直接终止进程,并不会在退出进程前会做任何收尾工作。
return、exit和_exit之间的区别与联系
return、exit和_exit之间的区别
只有在main函数当中的return才能起到退出进程的作用,子函数当中return不能退出进程,而exit函数和_exit函数在代码中的任何地方使用都可以起到退出进程的作用。
使用exit函数退出进程前,exit函数会执行用户定义的清理函数、冲刷缓冲,关闭流等操作,然后再终止进程,而_exit函数会直接终止进程,不会做任何收尾工作。
return、exit和_exit之间的联系
执行return num等同于执行exit(num),因为调用main函数运行结束后,会将main函数的返回值当做exit的参数来调用exit函数。
使用exit函数退出进程前,exit函数会先执行用户定义的清理函数、冲刷缓冲,关闭流等操作,然后再调用_exit函数终止进程。
#include
void exit(int status);
#include
void _exit(int status);
#include
#includepid_t wait(int *wstatus);
#include
#includepid_t waitpid(pid_t pid, int *status , int options)
功能:用来操作文件,本质是替换原来进程的内容,运行调用的进程
所需头文件 | #include |
函数说明 | 执行文件 |
函数原型 | 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 execve(const char *path, char *const argv[], char *const envp[]); | |
函数返回值 | 成功:函数不返回 |
失败:返回-1,失败原因记录在error中 |
常用的就前两个函数
l(list) : 表示参数采用列表;
v(vector) : 参数用数组;
p(path) : 有p自动搜索环境变量PATH;
e(env) : 表示自己维护环境变量;
一般操作
#include
#include
int main(int argc, char *argv[])
{
char *env[] = {"tiejiaxiaobao=3.1415", NULL};
char* buf[3] = {"ls", "-l", NULL};
// execl("/bin/ls", "ls", "-l", NULL)
// execlp("ls", "ls", "-l", NULL);
//execvp("ls", argv+1);
execle("./app", "./app", "qingtingduizhang", NULL, env);
printf("hello\n");
return 0;
}
#include
int main()
{
execl("./a.out","./a.out",NULL);
return 0;
}
#include int pthread_create ( pthread_t * thread , const pthread_attr_t * attr , void * ( * start_routine ) ( void * ), void * arg );编译要加-lpthread
start_routine:新创建线程从此函数开始运行。无参数是arg设为NULL即可。
arg:start_rtn函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数 时应当使用结构体传入。
#include
#include
#include
#include
void* thread_func(void* arg)
{
int b = 6;
while(1)
{
printf("%d\n", *(int*)arg);
//pthread_exit("hello");
pthread_exit(&b);
}
}
int main(int argc, char *argv[])
{
int a = 5;
pthread_t thread;
if( 0 != pthread_create(&thread, NULL, thread_func, &a))
{
printf("create is default\n");
return -1;
}
/*
void* val;
if(0 != pthread_join(thread, &val))
{
printf("join default\n");
return -1;
}
printf("%s\n", (char*)val);
*/
void* val;
if(0 != pthread_join(thread, &val))
{
printf("join default\n");
return -1;
}
printf("%d\n", *(int*)val);
while(1)
{
a++;
}
return 0;
}
#include
void pthread_exit(void *retval);
说明:pthread_exit里面的东西就是线程退出状态,我们可以在主函数里打印退出状态
根据上面示例,为什么下面代码打印的退出状态为什么不是6?
说明:因为b=6是局部变量,函数调用完就结束了,所以后面打印的退出状态是随机分配的,而hello在常量区
#include
int pthread_join(pthread_t thread, void **retval);
返回值:0是成功,非0是失败
#include
#include
#include
#include
void* thread_func(void* arg)
{
int b = 6;
while(1)
{
printf("%d\n", *(int*)arg);
//pthread_exit("hello");
pthread_exit(&b);
}
}
int main(int argc, char *argv[])
{
int a = 5;
pthread_t thread;
if( 0 != pthread_create(&thread, NULL, thread_func, &a))
{
printf("create is default\n");
return -1;
}
/*
void* val;
if(0 != pthread_join(thread, &val))
{
printf("join default\n");
return -1;
}
printf("%s\n", (char*)val);
*/
void* val;
if(0 != pthread_join(thread, &val))
{
printf("join default\n");
return -1;
}
printf("%d\n", *(int*)val);
while(1)
{
a++;
}
return 0;
}
#include
int pthread_detach(pthread_t thread);
注意:
1、要锁住线程需要同一把锁 ,所以一般锁的声明定义在全局
2、锁的初始化要在线程创建之前
概念:是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。
#include pthread_mutex_init ( pthread_mutex_t * _mutex , const pthread_mutex_t * mutexatter );
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
#include int pthread_mutex_unlock ( pthread_mutex_t * mutex );
#include
pthread_mutex_destroy (pthread_mutex_t *mutex);
互斥锁示例
#include
#include
#include
int a = 0;
pthread_mutex_t mutex;
void* thread_func(void * arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
a *= 2 ;
if( a % 2 != 0)
printf("A\n");
pthread_mutex_unlock(&mutex);
}
}
int main(int argc, char *argv[])
{
pthread_t thread;
if(0 != pthread_mutex_init(&mutex, NULL))
{
printf("mutex init if default\n");
exit(1);
}
if(0 != pthread_create(&thread, NULL, thread_func, NULL))
{
printf("create is default\n");
exit(1);
}
while(1)
{
pthread_mutex_lock(&mutex);
a = a*2 + 1;
if(a % 2 == 0)
printf("B\n");
pthread_mutex_unlock(&mutex);
}
if( 0 != pthread_mutex_destroy(&mutex))
{
printf("destor is default\n");
exit(1);
}
return 0;
}
注意:同步锁只能确保谁先谁后,但不保证生产1个,消费1个,可能会生产多个
概念:是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
#include
int sem_post(sem_t *sem);
注意:
sem_post()
函数用于增加(释放)由信号量表示的资源计数。每次调用sem_post()
函数都会将信号量的计数值加1,表示有一个资源可用。
sem_post()
函数调用后,如果有其他线程在等待该信号量(通过sem_wait()
函数),则其中一个等待线程会被唤醒,并继续执行。如果没有线程在等待该信号量,那么信号量的计数值就会增加,但不会有任何其他操作被阻塞。需要注意的是,
sem_post()
函数并不能保证某个特定的线程会被唤醒,它只是通知等待线程中的一个可以继续执行。具体唤醒哪个线程是由操作系统调度决定的。因此,
sem_post()
函数并不会阻塞当前线程,它只是简单地增加信号量的计数,可能导致等待线程被唤醒。
#include
int sem_wait(sem_t *sem);
同步锁示例
结果:终端输入一次,程序就打印一次
下面代码为什么不是依次打印的?(打印了多次 )
分析:因为同步锁只能确保对共享资源访问的先后顺序,只有生产了才能消费,但是生产了多少次不确定,sem_post函数是非阻塞且用于增加(释放)由信号量表示的资源计数。每次调用
sem_post()
函数都会将信号量的计数值加1,表示有一个资源可用,所以a++就生产了多次,信号量也有很多次,打印了许多相同的值。而上面程序之所以可以按顺序,是因为gets函数是阻塞函数,会等待输入
正确代码
#include
#include
#include
#include
char buf[100];
int a;
sem_t sem_r, sem_w;
void* thread_func(void * arg)
{
while(1)
{
if(-1 == sem_wait(&sem_r))
{
perror("wait");
pthread_exit(NULL);
}
/*
puts(buf);
*/
printf("a=%d\n", a);
if(0 != sem_post(&sem_w))
{
perror("post");
exit(1);
}
}
}
int main(int argc, char *argv[])
{
pthread_t thread;
if(0 != sem_init(&sem_r, 0, 0))
{
perror("sem_init");
exit(1);
}
if(0 != sem_init(&sem_w, 0, 1))
{
perror("sem_init");
exit(1);
}
if(0 != pthread_create(&thread, NULL, thread_func, NULL))
{
printf("create is default\n");
exit(1);
}
while(1)
{
//gets(buf);
if(-1 == sem_wait(&sem_w))
{
perror("wait");
pthread_exit(NULL);
}
a++;
if(0 != sem_post(&sem_r))
{
perror("post");
exit(1);
}
}
return 0;
}
分析:定义两个sem,将其中一个sem里面的资源设置为1,当两个线程都在wait时就会去争夺资源,而子线程资源为0,主线程资源为1,所以先执行主线程。
结果:有序的
本质通过内核来进行通信,所以必须要用文件IO进行系统调用
1. 有唯一读端和写端,相当于队列2. 当读端不存在,写端存在,再写入就会管道破裂(向进程发送 信号)3 写端不存在读端存在,读端会把最后的消息读出来后返回 0 ;
概念:文件系统不可见,只支持亲缘进程通信(只能在一个文件中通信,不能 1.c 和 2.c 之间通信)
int pipe(int pipefd[2]);
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fds[2];
if(-1 == pipe(fds)) //fork不会拷贝内核的内容
{
perror("pipe");
exit(1);
}
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid)
{
close(fds[0]);//关闭读端
while(1)
{
char buf[100];
gets(buf);
write(fds[1], buf, sizeof(buf));
}
exit(1);
}
else
{
close(fds[1]);//关闭写端
while(1)
{
char buf[100];
read(fds[0], buf, sizeof(buf));
puts(buf);
}
}
return 0;
}
概念:文件系统可见, 支持非亲缘(可以由 1.c 和 2.c 之间通信,也可以亲缘之间通信)
创建方法一:使用命令
使用mkfifo创建有名管道
创建方法二:使用函数
int mkfifo(const char *pathname, mode_t mode);
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/myfifo"
int main() {
int fd;
char message[] = "Hello, named pipe!";
char readbuf[80];
// 创建命名管道
mkfifo(FIFO_NAME, 0666);
// 打开命名管道以供写入
fd = open(FIFO_NAME, O_WRONLY);
if (fd == -1) {
perror("open");
exit(1);
}
// 写入数据到命名管道
write(fd, message, sizeof(message));
printf("Sent message: %s\n", message);
// 关闭命名管道
close(fd);
// 打开命名管道以供读取
fd = open(FIFO_NAME, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
// 从命名管道中读取数据
read(fd, readbuf, sizeof(readbuf));
printf("Received message: %s\n", readbuf);
// 关闭命名管道
close(fd);
// 删除命名管道
unlink(FIFO_NAME);
return 0;
}
int kill ( pid_t pid , int sig );
int raise(int sig);
#include
#include
#include
#include
int main(int argc, char *argv[])
{
sleep(3);
/*
if(-1 == kill(getpid(), SIGKILL))
perror("kill");
*/
if(-1 == raise(SIGKILL))
perror("raise");
while(1);
return 0;
}
typedef void ( * sighandler_t )( int );sighandler_t signal ( int signum , sighandler_t handler );
#include
#include
void sighandler_t(int sig)
{
printf("hello\n");
}
int main(int argc, char *argv[])
{
/*
if(SIG_DFL != signal(SIGINT, SIG_IGN))
perror("signal");
*/
if(SIG_DFL != signal(SIGINT, sighandler_t))
perror("signal");
while(1);
return 0;
}
unsigned int alarm(unsigned int seconds)
#include
#include
#include
void func(int sig)
{
printf("hello\n");
alarm(3);
}
int main(int argc, char *argv[])
{
signal(SIGALRM, func);
alarm(3);
while(1);
return 0;
}
速度非常快,由于共享内存是直接访问内存(通过内存映射方式),因此它的通信效率比管道高,适用于大量数据的传输。而管道则需要进行数据拷贝,放在缓存区,所以相对来说通信效率较低。
步骤:1.创建ipc对象/打开共享内存,2.申请共享内存,3.内存映射,4.取消映射,5.删除共享内存
IPC对象的概念:
在C语言中,IPC(Inter-Process Communication)对象是用于进程间通信的一种机制,它可以让多个进程之间共享数据或者协调操作。常见的IPC对象包括:
信号量(semaphore):用于进程间的同步和互斥,可以通过对信号量的操作来实现进程的等待和唤醒。
共享内存(shared memory):允许多个进程访问同一块内存区域,可以通过读写共享内存来进行数据交换。
消息队列(message queue):用于进程间的异步通信,可以通过消息队列来传递数据和控制信息。
管道(pipe):用于进程间的单向通信,可以在一个方向上传输数据。
套接字(socket):用于进程间或网络间的通信,可以建立客户端-服务器模型或点对点通信。
key_t ftok(const char *pathname, int proj_id);
int shmget(key_t key, int size, int shmflg);
共享内存是不可见的,使用ipcs命令查看共享内存:
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
#include
#include
#include
#include
#include
#include
#include
#include
#define SHMSIZE 10
int main(int argc, char *argv[])
{
key_t key = ftok("1.c", 'a');
if(-1 == key)
{
perror("key");
exit(1);
}
int shmid = shmget(key, SHMSIZE, IPC_CREAT| 0666);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
void* addr = NULL;
if(NULL == (addr = shmat(shmid, NULL, 0)))
{
perror("shmat");
exit(1);
}
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid)
{
while(1)
{
puts((char*)addr);
}
exit(1);
}
else
{
int i = 3;
while(i--)
{
fgets((char*)addr, SHMSIZE, stdin);
}
if(-1 == kill(pid, SIGKILL))
{
perror("kill");
exit(1);
}
wait(NULL);
if(-1 == shmdt(addr))
{
perror("shmdt");
exit(1);
}
if(-1 == shmctl(shmid, IPC_RMID, NULL))
{
perror("shmctl");
exit(1);
}
exit(0);
}
return 0;
}
步骤:创建消息队列,读写,删除
int msgget(key_t key, int flags);
int msgsnd(int msgid, void* buf, int size, int flags);
flags:0阻塞 NOWAIT
int msgrcv(int msgid, void* buf, int size,long type, int flags);
int msgctl ( int msgid , int cmd , struct msqid_ds * buf );
#include
#include
#include
#include
#include
#include
#define MSG_SIZE 1024
#define MSG_TYPE 1
struct message {
long mtype;
char mtext[MSG_SIZE];
};
int main() {
int msgqid; // 消息队列标识符
struct message msg;
// 创建或获取消息队列
key_t key = ftok(".", 'M');
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
msgqid = msgget(key, IPC_CREAT | 0666);
if (msgqid == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
// 发送消息
strcpy(msg.mtext, "Hello, world!");
msg.mtype = MSG_TYPE;
if (msgsnd(msgqid, &msg, strlen(msg.mtext)+1, 0) == -1) {
perror("msgsnd");
exit(EXIT_FAILURE);
}
printf("Sent message: %s\n", msg.mtext);
// 接收消息
if (msgrcv(msgqid, &msg, sizeof(msg.mtext), MSG_TYPE, 0) == -1) {
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("Received message: %s\n", msg.mtext);
// 删除消息队列
if (msgctl(msgqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(EXIT_FAILURE);
}
return 0;
}
信号灯(Semaphore)是一种同步机制,用于协调多个进程或线程对共享资源的访问。
信号灯集(Semaphore Set)则是多个信号灯的集合,它们通常被用来控制多个进程或线程对多个共享资源的访问,一般和共享内存一起使用。
步骤:创建信号灯集,初始化信号灯集,pv操作信号灯(P操作会将信号灯的值减1,V操作会将信号灯的值加1),删除信号灯集
int semget(key_t key, int semsize, int flags);
int semctl(int semid, int semnum, int cmd....../union semun );
int semop(int semid, struct sembuf *opbuf, int len);
#include
#include
#include
#include
#include
#include
#define NUM_SEMAPHORES 2
// 定义信号灯集操作的结构体
struct sembuf sem_op;
int main() {
int sem_id; // 信号灯集标识符
key_t key; // 信号灯集键值
// 创建或获取信号灯集
key = ftok(".", 'S'); // 创建信号灯集键值
sem_id = semget(key, NUM_SEMAPHORES, IPC_CREAT | 0666); // 创建或获取信号灯集
if (sem_id == -1) {
perror("semget");
exit(1);
}
// 初始化信号灯集中的信号灯
unsigned short init_values[NUM_SEMAPHORES] = {1, 0}; // 初始化为1和0
if (semctl(sem_id, 0, SETALL, init_values) == -1) {
perror("semctl");
exit(1);
}
// 进程间对信号灯进行操作
// P操作,将第一个信号灯的值减1
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop P");
exit(1);
}
printf("Process 1 is in critical section\n");
sleep(2); // 模拟临界区操作
printf("Process 1 releases the resource\n");
// V操作,将第二个信号灯的值加1
sem_op.sem_num = 1;
sem_op.sem_op = 1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop V");
exit(1);
}
// 删除信号灯集
if (semctl(sem_id, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
exit(1);
}
return 0;
}
生产者生产两次,消费一次代码示例:
#include
#include
#include
#include
#include
#include
#define NUM_SEMAPHORES 3
// 定义信号灯集操作的结构体
struct sembuf sem_op;
int main() {
int sem_id; // 信号灯集标识符
key_t key; // 信号灯集键值
// 创建或获取信号灯集
key = ftok(".", 'S'); // 创建信号灯集键值
sem_id = semget(key, NUM_SEMAPHORES, IPC_CREAT | 0666); // 创建或获取信号灯集
if (sem_id == -1) {
perror("semget");
exit(1);
}
// 初始化信号灯集中的信号灯
unsigned short init_values[NUM_SEMAPHORES] = {2, 0, 1}; // 初始化为2、0和1
if (semctl(sem_id, 0, SETALL, init_values) == -1) {
perror("semctl");
exit(1);
}
// 生产者
if (fork() == 0) {
for (int i = 0; i < 2; ++i) {
// P操作,申请空闲资源
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop P");
exit(1);
}
// 生产
printf("Producer produces an item\n");
// V操作,增加一个产品
sem_op.sem_num = 1;
sem_op.sem_op = 1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop V");
exit(1);
}
}
}
// 消费者
else {
// P操作,等待有产品可消费
sem_op.sem_num = 1;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop P");
exit(1);
}
// 消费
printf("Consumer consumes an item\n");
// V操作,释放一个空闲资源
sem_op.sem_num = 0;
sem_op.sem_op = 1;
sem_op.sem_flg = 0;
if (semop(sem_id, &sem_op, 1) == -1) {
perror("semop V");
exit(1);
}
}
// 删除信号灯集
if (semctl(sem_id, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
exit(1);
}
return 0;
}
#include
#include
#include
#include
#include
#include
#define SIZE 100
#define SEMSIZE 2
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
enum sem_name{
SEM_R ,
SEM_W
};
void sem_op(const int semid,const short op,const short semnumber)
{
//第一个参数是给⬇灯做操作,第二个是做什么操作(pv),第三个是阻塞还是非阻塞
struct sembuf buf = {semnumber, op, 0};
if(-1 == semop(semid, &buf, 1))
{
perror("semop");
exit(1);
}
return ;
}
int main(int argc, char *argv[])
{
//获取一个key值
key_t key = ftok("1.c", 'a');
if(-1 == key)
{
perror("ftok");
exit(1);
}
//创建信号灯集
int semid = semget(key, SEMSIZE, IPC_CREAT|0666);
if(-1 == semid)
{
perror("semget");
exit(1);
}
//创建给信号灯初始化的值
union semun my_semun = {0};
//把第0个信号灯设置初始值为0
if(-1 == semctl(semid, SEM_R, SETVAL, my_semun))
{
perror("set val r");
exit(1);
}
//把第1个信号灯设置初值为1
my_semun.val = 1;
if(-1 == semctl(semid, SEM_W, SETVAL, my_semun))
{
perror("set val w");
exit(1);
}
//创建贡献内存
int shmid = shmget(key, SIZE, IPC_CREAT|0666);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
//地址映射
void* addr = shmat(shmid, NULL, 0);
if(NULL == addr)
{
perror("shmat");
exit(1);
}
while(1)
{
//消费写的资源,如果没有写资源就阻塞
sem_op(semid, -1, SEM_W);
fgets((char*)addr, SIZE, stdin);
if(strstr((char*)addr, "quit"))
break;
//生产读的资源
sem_op(semid, 1, SEM_R);
}
//取消映射
if(-1 == shmdt(addr))
{
perror("shmdt");
exit(1);
}
//删除共享内存
if(-1 == shmctl(shmid, IPC_RMID, NULL))
{
perror("shmctl");
exit(1);
}
exit(0);
return 0;
}
服务器:
步骤:1.创建套接字 2.绑定套接字 3.监听套接字 4.等待连接
int socket ( int domain , int type , int protocol );
int bind ( int sockfd , const struct sockaddr * addr , socklen_t addrlen );
int listen ( int sockfd , int backlog );
int accept ( int sockfd , struct sockaddr * addr , socklen_t * addrlen );
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7777);
server_addr.sin_addr.s_addr = INADDR_ANY;
if(-1 == bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
perror("bind");
return -1;
}
if(-1 == listen(sockfd, 4))
{
perror("listen");
return -1;
}
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
printf("wait for a client\n");
int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &len);
if(-1 == connfd)
{
perror("accept");
return -1;
}
printf("connected a client ip=%s port=%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
while(1)
{
char buf[100];
read(connfd, buf, sizeof(buf));
puts(buf);
}
return 0;
}
客户端:
步骤:1.创建套接字 2.绑定套接字(可以省略) 3.连接服务器
int connect ( int sockfd , const struct sockaddr * addr , socklen_t addrlen );
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons((uint16_t)atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(-1 == connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
perror("connect");
return -1;
}
while(1)
{
char buf[100];
fgets(buf,sizeof(buf),stdin);
write(sockfd, buf, sizeof(buf));
}
return 0;
}
第一次握手:刚开始时,服务器和客户端都是断开的,当进行三次握手时,客户端先向服务器发送连接请求,并将SYN置为1,客户端进入 SYN-SENT(同步已发送) 状态。
第二次握手:服务器收到 syn 包,要确认建立连接,所以服务器发送一个 SYN+ACK 包给客户端,服务器进入 SYN-RCVD(同步收到)状态
第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK,此包发送完毕,客户端和服务器进入 ESTABLISHED(已建立连接)状态
第一次挥手:刚开始通信双方是 ESTABLISHED(已建立连接)状态,当进行四次挥手时,客户端的应用进程先向服务器发出连接释放报文段 ,并将FIN置为1,客户端进入 FIN-WAIT(终止等待)状态,客户端此时处于半关闭状态(应用层无法接收数据但底层还可以接收数据)
第二次挥手:服务器接收到客户端的 FIN 报文后会做两件事情:1.对客服端的FIN做出确认 2.向客户端发出连接释放报文段。所以,服务器向客户端发送确认释放连接报文,将ACK置为1,服务器进入 CLOSE-WAIT(关闭等待)状态,客户端进入 FIN-WAIT(终止等待)状态
第三次挥手:服务器向客户端发送确认释放连接报文,并将FIN置为1,之后服务器进入LASK_ACK(最后确认)状态,等待客户端的确认。
第四次挥手:客户端收到来自服务器的连接释放(FIN)报文段后,会向服务器发送一个ACK应答报文段,之后客户端进入TIME_WAIT(时间等待)状态,此时的TCP还未释放掉,需要等待2MSL后,客户端才进入CLOSE状态。服务器收到ACK应答报文段后,服务器就进入CLOSE(关闭)状态,到此服务器的连接已经完成关闭。
每次连接一个客户端就开一个进程
一般框架:
1.创建套接字
2.绑定套接字
3.监听套接字
signal(17,func)
while(1)
{
4.被动等待连接
5.开辟子进程,做服务
//waitpid();效率低下,用17号信号
}
服务器:
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
void sighandler(int sig)
{
wait(NULL);
}
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
if(-1 == listen(sockfd, 4))
{
perror("listen");
return -1;
}
if(-1 == signal(SIGCHLD, sighandler))
{
perror("signal");
return -1;
}
while(1)
{
int connfd = accept(sockfd, NULL, NULL);
if(-1 == connfd)
{
perror("accept");
return -1;
}
printf("connected a client\n");
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
return -1;
}
else if(0 == pid)
{
while(1)
{
char buf[100];
if(0 == read(connfd, buf, sizeof(buf)))
{
close(connfd);
exit(0);
}
printf("buf=%s\n", buf);
if(strstr(buf,"time"))
{
time_t t;
time(&t);
strcpy(buf, ctime(&t));
write(connfd, buf, sizeof(buf));
}
else
{
puts(buf);
}
}
}
}
close(sockfd);
exit(0);
return 0;
}
客户端:
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
if(-1 == connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("connect");
return -1;
}
while(1)
{
char buf[100];
fgets(buf, sizeof(buf),stdin);
write(sockfd, buf, sizeof(buf));
if(strstr(buf, "time"))
{
read(sockfd, buf, sizeof(buf));
puts(buf);
}
else if(strstr(buf, "quit"))
{
break;
}
}
close(sockfd);
exit(0);
return 0;
}
一般框架:
1.创建套接字
2.绑定套接字
3.监听套接字
while(1)
{
4.被动等待连接
5.线程,做服务
//join
detach
}
服务器:
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void* thread_func(void* arg)
{
int connfd = *(int*)arg;
while(1)
{
char buf[100];
if(0 == read(connfd, buf, sizeof(buf)))
{
close(connfd);
pthread_exit(NULL);
}
printf("buf=%s\n", buf);
if(strstr(buf,"time"))
{
time_t t;
time(&t);
strcpy(buf, ctime(&t));
write(connfd, buf, sizeof(buf));
}
else
{
puts(buf);
}
}
}
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
if(-1 == listen(sockfd, 4))
{
perror("listen");
return -1;
}
while(1)
{
int connfd = accept(sockfd, NULL, NULL);
if(-1 == connfd)
{
perror("accept");
return -1;
}
printf("connected a client\n");
pthread_t thread;
if(0 != pthread_create(&thread, NULL, thread_func, &connfd))
{
perror("create");
}
if(0 != pthread_detach(thread))
{
perror("detach");
}
}
close(sockfd);
exit(0);
return 0;
}
客户端:
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
if(-1 == connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("connect");
return -1;
}
while(1)
{
char buf[100];
fgets(buf, sizeof(buf),stdin);
write(sockfd, buf, sizeof(buf));
if(strstr(buf, "time"))
{
read(sockfd, buf, sizeof(buf));
puts(buf);
}
else if(strstr(buf, "quit"))
{
break;
}
}
close(sockfd);
exit(0);
return 0;
}
可以同时操作多个文件描述符,可以设置阻塞或非阻塞
一般框架:
int select ( int nfds , fd_set * readfds , fd_set * writefds , fd_set * exceptfds , struct timeval * timeout );
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
//2.绑定套接字
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
//3.监听套接字
if(-1 == listen(sockfd, 4))
{
perror("listen");
return -1;
}
//4.创建两个位图,一个用来保存关心的文件描述符,一个用来做内核拷贝的空间 fd_set A ,B;
fd_set readfds, tempfds;
//5.清0位图 void FD_ZERO(fd_set *set);
FD_ZERO(&readfds);
//6.把监听套接字加入位图 void FD_SET(int fd, fd_set *set);
FD_SET(sockfd, &readfds);
int nfds = sockfd+1;
while(1)
{
tempfds = readfds;
//7.把位图传入内核等待响应 select
if(-1 == select(nfds, &tempfds, NULL, NULL, NULL))
{
perror("select");
return -1;
}
//8.遍历位图,判断对应位置是否为1 int FD_ISSET(int fd, fd_set *set);
int i;
for(i = sockfd; i < nfds; i++)
{
if(FD_ISSET(i, &tempfds))
{
//9.判断准备好的这个文件描述符是哪种类型的文件描述符
if(i == sockfd)
{
//如果是监听套接字就accept(把新的文件描述符加入关心的位图)
int connfd = accept(i, NULL, NULL);
if(-1 == connfd)
{
perror("accept");
}
FD_SET(connfd, &readfds);
(connfd + 1 > nfds) ? (nfds = connfd+1) : (nfds = nfds);
}
else
{
//如果是connfd就做相应功能 (如果客户端退出需要删除相应的位图) void FD_CLR(int fd, fd_set *set);
char buf[100];
if(0 == read(i, buf, sizeof(buf)))
{
FD_CLR(i, &readfds);
close(i);
}
printf("buf=%s\n", buf);
if(strstr(buf,"time"))
{
time_t t;
time(&t);
strcpy(buf, ctime(&t));
write(i, buf, sizeof(buf));
}
else
{
puts(buf);
}
}
}
}
}
}
两次拷贝,轮询,大小没有限定(结构体数组)
一般框架:
int poll ( struct pollfd * fds , nfds_t nfds , int timeout );
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 2048
int main()
{
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("0");
//2.绑定套接字
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
//3.监听套接字
if(-1 == listen(sockfd, 4))
{
perror("listen");
return -1;
}
//4.创建两个位图,一个用来保存关心的文件描述符,一个用来做内核拷贝的空间 fd_set A ,B;
//fd_set readfds, tempfds;
struct pollfd readfds[SIZE];
//5.清0位图 void FD_ZERO(fd_set *set);
//FD_ZERO(&readfds);
int i;
for(i = 0; i < SIZE; i++)
readfds[i].fd = -1;
//6.把监听套接字加入位图 void FD_SET(int fd, fd_set *set);
//FD_SET(sockfd, &readfds);
//int nfds = sockfd+1;
int index = 0;
readfds[index].fd = sockfd;
readfds[index].events = POLLIN;
index++;
while(1)
{
//tempfds = readfds;
//7.把位图传入内核等待响应 select
/*
if(-1 == select(nfds, &tempfds, NULL, NULL, NULL))
{
perror("select");
return -1;
}
*/
if(-1 == poll(readfds, index, -1))
{
perror("poll");
return -1;
}
//8.遍历位图,判断对应位置是否为1 int FD_ISSET(int fd, fd_set *set);
for(i = 0; i < index; i++)
{
//if(FD_ISSET(i, &tempfds))
if(readfds[i].revents & POLLIN)
{
//9.判断准备好的这个文件描述符是哪种类型的文件描述符
if(readfds[i].fd == sockfd)
{
//如果是监听套接字就accept(把新的文件描述符加入关心的位图)
int connfd = accept(readfds[i].fd, NULL, NULL);
if(-1 == connfd)
{
perror("accept");
}
//FD_SET(connfd, &readfds);
//(connfd + 1 > nfds) ? (nfds = connfd+1) : (nfds = nfds);
int j;
for(j = 0; j < index; j++)
{
if(readfds[j].fd == -1)
{
readfds[j].fd = connfd;
readfds[j].events = POLLIN;
break;
}
}
if(j == index)
{
readfds[index].fd = connfd;
readfds[index].events = POLLIN;
index++;
}
}
else
{
//如果是connfd就做相应功能 (如果客户端退出需要删除相应的位图) void FD_CLR(int fd, fd_set *set);
char buf[100];
if(0 == read(readfds[i].fd, buf, sizeof(buf)))
{
// FD_CLR(i, &readfds);
close(readfds[i].fd);
readfds[i].fd = -1;
}
printf("buf=%s\n", buf);
if(strstr(buf,"time"))
{
time_t t;
time(&t);
strcpy(buf, ctime(&t));
write(readfds[i].fd, buf, sizeof(buf));
}
else
{
puts(buf);
}
}
}
}
}
}
int epoll_create(int size);
int epoll_ctl ( int epfd , int op , int fd , struct epoll_event * event );
int epoll_wait ( int epfd , struct epoll_event * events , int maxsize, int timeout );
无连接,不可靠,有失序,数据报,不稳定
1、创建套接字
2、发送信息
ssize_t sendto ( int sockfd , const void * buf , size_t len , int flags , const struct sockaddr * dest_addr , socklen_t addrlen );
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(addr);
char buf[100];
while(1)
{
fgets(buf,sizeof(buf),stdin);
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
if(strstr(buf, "time"))
{
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
puts(buf);
}
}
return 0;
}
服务器:
1.创建套接字
2.绑定
3.接收
size_t recvfrom ( int sockfd , void * buf , size_t len , int flags , struct sockaddr * src_addr , socklen_t * addrlen );
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(addr);
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
char buf[100];
while(1)
{
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &len);
if(strstr(buf, "time"))
{
time_t T;
time(&T);
strcpy(buf, ctime(&T));
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
}
}
return 0;
}
服务器可以做发送方,也可以做接收方,但一般是发送方,此处是服务器作为接收方
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(addr);
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
char buf[100];
while(1)
{
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &len);
if(strstr(buf, "time"))
{
time_t T;
time(&T);
strcpy(buf, ctime(&T));
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
}
puts(buf);
}
return 0;
}
客户端可以做发送方,也可以做接收方,但一般是发送方,此处是客户端作为发送方
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = inet_addr("192.168.2.255");
socklen_t len = sizeof(addr);
int val = 1;
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,&val,sizeof(val)))
{
perror("opt");
return -1;
}
char buf[100];
while(1)
{
fgets(buf,sizeof(buf),stdin);
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
if(strstr(buf, "time"))
{
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
puts(buf);
}
}
return 0;
}
0.0.0.0 - 127.255.255.255 0.0.0.0本机 127.0.0.1本机回环 127.255.255.255.广播地址
128.0.0.0-191.255.255.255
C类 小型网络 前三个字节为网络号, 后一个字节为主机号110
192.0.0.0 - 223.255.255
D类 组播地址 1110
224.0.0.0 - 239.255.255.255
E类 11110 保留地址
nt setsockopt ( int sockfd , int level , int optname , const void * optval , socklen_t optlen );设置属性表: level======================================================================
SOL_SOCKET 参数optname宏的作用 对应参数optval的类型 SO_BROADCAST 允许发送广播数据 int SO_DEBUG 循序调试 int SO_DONTROUTE 不查找路由 int SO_ERROR 获的套接字错误 int SO_KEEPALIVE 保持连接 int SO_LINGER 延迟关闭连接 struct linger SO_OOBINLINE 带外数据放入正常数据流 int SO_RCVBUF 接收缓冲区大小 int SO_SNDBUF 发送缓冲区大小 int SO_RCVLOWAT 接收缓冲区下限 int SO_SNDWAIT 发送缓冲区下限 int SO_RCVTIMEO 接收超时 struct timeval SO_SNDTIMEO 发送超时 struct timeval SO_REUSEADDR 允许重用本机地址和端口 int SO_TYPE 获得套接字类型 int SO_BSDCOMPAT 与 BSD 系统兼容 int
IPPROTO_IP 参数 optname 宏的作用 对应参数 optval 的类型 IP_ADD_MEMBERSHIP 加入到组播组中 struct ip_mreq IP_MULTICAST_IF 允许开启组播报文的接口 struct ip_mreq======================================================================typedef uint32_t in_addr_t ;struct in_addr{in_addr_t s_addr ;};// ip_mreq 是一个旧的数据结构,但目前仍然可用struct ip_mreq{/* IP multicast address of group. */struct in_addr imr_multiaddr ;// 设置加入多播组的的网卡 ip, 注意这里并不表示 socket 同该网卡绑定// 该 socket 仍然能够接收到不是该网卡的数据包,该设置仅仅表示该 ip// 对应的网卡能够接收对应多播组的数据包struct in_addr imr_interface ;};
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(addr);
if(-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
struct ip_mreq muladdr;
muladdr.imr_multiaddr.s_addr = inet_addr("230.1.1.1");
muladdr.imr_interface.s_addr = INADDR_ANY;
if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&muladdr, sizeof(muladdr)))
{
perror("opt");
return -1;
}
char buf[100];
while(1)
{
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &len);
if(strstr(buf, "time"))
{
time_t T;
time(&T);
strcpy(buf, ctime(&T));
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
}
puts(buf);
}
return 0;
}
#include
#include
#include /* See NOTES */
#include
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("sokect");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = inet_addr("230.1.1.1");
socklen_t len = sizeof(addr);
struct ip_mreq muladdr;
muladdr.imr_multiaddr.s_addr = inet_addr("230.1.1.1");
muladdr.imr_interface.s_addr = INADDR_ANY;
if(-1 == setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF,&muladdr, sizeof(muladdr)))
{
perror("opt");
return -1;
}
char buf[100];
while(1)
{
fgets(buf,sizeof(buf),stdin);
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, len);
if(strstr(buf, "time"))
{
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
puts(buf);
}
}
return 0;
}
setsockoptio多路复用信号 alarm
作用:相同主机进行交互(传输文件描述符,传输软件证书)
接收方:
#include
#include
#include /* See NOTES */
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,"1.txt");
if(-1 == bind(sockfd,(struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return -1;
}
char buf[100];
while(1)
{
recvfrom(sockfd, buf,sizeof(buf),0, NULL, NULL);
puts(buf);
}
return 0;
}
发送方:
#include
#include
#include /* See NOTES */
#include
#include
int main(int argc, char *argv[])
{
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(-1 == sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,"1.txt");
char buf[100];
while(1)
{
fgets(buf,sizeof(buf),stdin);
sendto(sockfd, buf,sizeof(buf),0, (struct sockaddr*)&addr, sizeof(addr));
}
return 0;
}
sql语句
创建 create table ligintable(name char, passwd char);增 insert into ligintable values("xiaoming", "123");删 delete from ligintable where name="xiaoming";改 update ligintable set name="xiaohua" where name="xiaoming";查 select *from ligintable;select *from ligintable where name="xiaoming" and passwd="123";select *from ligintable where name="xiaoming" or passwd="123";删除表 drop table + tablename;
数据库操作函数
int sqlite3_open ( const char * zFilename , //< 文件名字 sqlite3 ** ppDb //< 操作句柄 ) ;// 返回值:成功 0 , 失败非 0
const char *sqlite3_errmsg(sqlite3 *db);
//打印错误信息
int sqlite3_exec (sqlite3 * db , /* 数据库的句柄 */const char * zSql , /* sql 语句 */sqlite3_callback xCallback , /* 做查询的回调函数 */void * pArg , /* 回调函数的参数 */char ** pzErrMsg /* 错误信息的返回 */);/*如果不是做查找 第 3 个和第四个参数可以是 NULL成功返回 0 , 失败非 0*/
int sqlite3_get_table ( sqlite3 * db , char* sql , char* ** dResult , int * nRow , int * nColnm , char ** errmsg );/*db: 数据库sql: 控制语句**dResult: 二维数组nRow: 查询结果条数nColnm: 每条结果包含多少数据errmsg: 报错信息*/
sqlite3_close();
//关闭数据库
#include
#include
#define SIZE 100
int main(int argc, char *argv[])
{
sqlite3* db = NULL;
if(0 != sqlite3_open("./my.db", &db))
{
printf("%s\n", sqlite3_errmsg(db));
return -1;
}
char name[100];
char passwd[100];
char sql[SIZE+200];
fgets(name,sizeof(name),stdin);
fgets(passwd,sizeof(passwd),stdin);
sprintf(sql,"insert into ligintable values('%s', '%s');", name, passwd);
char*err = NULL;
if(0 != sqlite3_exec(db, sql, NULL, NULL, &err))
{
printf("%s\n", err);
return -1;
}
return 0;
}
#include
#include
#define SIZE 100
int main(int argc, char *argv[])
{
sqlite3* db = NULL;
if(0 != sqlite3_open("./my.db", &db))
{
printf("%s\n", sqlite3_errmsg(db));
return -1;
}
while(1)
{
char sql[200];
printf("input sql\n");
gets(sql);
puts(sql);
char*err = NULL;
if(0 != sqlite3_exec(db, sql, NULL, NULL, &err))
{
printf("%s\n", err);
}
}
return 0;
}
#include
#include
#include
#define SIZE 100
struct msg{
char name[100];
char passwd[100];
};
int cback(void* arg, int n, char** val, char** key)
{
//printf("arg=%d n=%d\n val=%s key=%s\n", *(int*)arg, n, val[1], key[1]);
/*
int i;
for(i = 0; i < n; i++)
{
printf("%s=%s ", key[i], val[i]);
}
printf("\n");
*/
if((strcmp(val[0], ((struct msg*)arg)->name) == 0) && (strcmp(val[1], ((struct msg*)arg)->passwd) == 0))
printf("login is ok\n");
else
printf("login is default\n");
return 0;
}
int main(int argc, char *argv[])
{
sqlite3* db = NULL;
if(0 != sqlite3_open("./my.db", &db))
{
printf("%s\n", sqlite3_errmsg(db));
return -1;
}
struct msg msgs;
printf("input name and passwd\n");
scanf("%s%s", msgs.name, msgs.passwd);
char sql[SIZE+200];
sprintf(sql,"select *from ligintable where name='%s' and passwd='%s';", msgs.name, msgs.passwd);
puts(sql);
/*
char*err = NULL;
int i = 5;
if(0 != sqlite3_exec(db, sql, cback, &msgs, &err))
{
printf("%s\n", err);
return -1;
}
*/
char**result = NULL;
int nrow, ncolnm;
char* err = NULL;
if(-1 == sqlite3_get_table(db,sql,&result,&nrow,&ncolnm,&err))
{
printf("%s\n", err);
}
/*
int i, j;
for(j = 0; j < nrow*ncolnm; j+=ncolnm)
{
for(i = 0; i < ncolnm; i++)
{
printf("%s=%s ", result[i], result[ncolnm+i+j]);
}
}
*/
int i;
for(i = 0; i < nrow*ncolnm+ncolnm; i++)
{
printf("%s\n", result[i]);
}
return 0;
}