习题来源于书籍和网络
1、编写一个孤儿进程,这个孤儿进程可以同时创建100个僵死进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int arg, char* args[])
{
pid_t pid = fork();
if(pid == -1){
return -1;
}
if(pid == 0)//子进程
{
printf("my child\n");
int i = 0;
for(; i < 100; i++)
{
pid_t cpid = fork();
if(cpid == -1)
{
printf("%s",strerror(errno));
}
if(cpid == 0)
{
printf("Grandson dead!\n");
exit(0);
}
}
pause();//因为子进程已死,所以要保留父进程才能看到僵死进程
}
if(pid > 0)//父进程
{
exit(0);
}
return 0;
}
2、两个程序a和b,b为a的子进程,在a程序中打开d.txt文件。在b中读取d.txt内容。
readProc.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int arg, char* args[])
{
int fd = open("d.txt",O_RDONLY);
pid_t pid = fork();
if(pid < 0)
{
printf("%s\n",strerror(errno));
}
if(pid == 0)//创建子进程
{
char sfd[10] = {0};
sprintf(sfd,"%d",fd);
char* const args_c[]={"writeProc",sfd,NULL};
int err = execve("writeProc",args_c,NULL);//创建b进程,传入参数 文件描述符。
if(err == -1)
{
printf("%s",strerror(errno));
}
}
return 0;
}
writeProc.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int arg, char* args[])
{
if(arg < 2) return -1;
char* buf[10] = {0};
while(read(atoi(args[1]),buf,sizeof(buf)-1))
{
printf("%s",buf);
memset(buf,0,sizeof(buf));
}
return 0;
}
3、子进程写数据父进程读取,无名管道的应用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
int main(int arg, char* args[])
{
int fd[2];
int *readfd = &fd[0];
int *writefd = &fd[1];
int rs = pipe(fd);
int status;
char buf[1024];
char buf2[1024];
if(rs < 0)
{
printf("%s",strerror(errno));
}
pid_t pid = fork();
if(pid < 0)
{
printf("%s",strerror(errno));
}
if(pid == 0)
{
close(*readfd);//子进程关闭读
while(1)
{
memset(buf,0,sizeof(buf));
read(0, buf, sizeof(buf));//从标准输入中读
write(*writefd,buf,strlen(buf));//写数据
}
}else if(pid > 0)
{
close(*writefd);//父进程关闭写
while(1)
{
int byte = read(*readfd,buf2,sizeof(buf2));
if(byte > 0)
{
printf("%s\n",buf2);
memset(buf2,0,sizeof(buf2));
}
}
wait(&status);
}
return 0;
}
4、有名管道读写。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
int openfile(char* path, int *fd, int type)
{
if(path == NULL || fd == NULL)
{
printf("pointer error!\n");
return -1;
}
if(type == 1){
*fd = open(path,O_RDONLY);
printf("read fd is %d \n", *fd);
}else
{
*fd = open(path,O_WRONLY);
printf("write fd is %d \n", *fd);
}
if(*fd < 0)
{
printf("openfile failed!\n");
return -1;
}
return 0;
}
int readfile(int *fd)
{
char buf[100];
int rs;
while(1)
{
memset(buf,0,sizeof(buf));
rs = read(*fd,buf,sizeof(buf));
printf("%s",buf);
}
return 0;
}
int writefile(int *fd)
{
char buf[100];
while(1)
{
memset(buf,0,sizeof(buf));
read(0,buf, sizeof(buf));
write(*fd,buf,strlen(buf));
}
return 0;
}
int main(int arg, char* args[])
{
if(arg < 3) return -1;
int fd;
int type = atoi(args[2]);
openfile(args[1],&fd,type);
if(fd < 0) return -1;
if(type == 1)//只读
{
readfile(&fd);
}else{//只写
writefile(&fd);
}
close(fd);
return 0;
}
5、共享内存读写。
创建共享内存
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int arg, char* args[])
{
int shmid = shmget(IPC_PRIVATE, 1024, 0666);//分配共享内存
if(shmid < 0)
{
return -1;
}
printf("ipcs create success!\n");
return 0;
}
读写共享内存
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <unistd.h>
int main(int arg, char* args[])
{
char* shumbuf = shmat(atoi(args[1]),0,0);//挂载共享内存
if(atoi(args[2]) == 0)
{
scanf("%s",shumbuf);
}else{
printf("%s\n",shumbuf);
}
shmdt(shumbuf);
return 0;
}
6、在linux下编写并使用一个库文件
头文件
#ifndef __LIB_UPPER_H__
#define __LIB_UPPER_H__
#include <stdio.h>
#include <string.h>
void upper(const char *src, char *desc);
#endif
源文件
#include "libupper.h"
void upper(const char *src, char *desc)
{
if(src == NULL || desc == NULL)
{
printf("NULL Pointer...");
return;
}
int len = strlen(src);
int i = 0;
char tmp;
for(; i < len; i++)
{
if(src[i] >= 'a' && src[i] <= 'z')
{
tmp = 'A' - 'a' + src[i];
}else
{
tmp = src[i];
}
desc[i] = tmp;
}
}
测试文件
#include <stdio.h>
#include <stdlib.h>
#include "libupper.h"
int main(int arg, char* args[])
{
char dest[100] = {0};
upper(args[1], dest);
printf("%s\n",dest);
return 0;
}
具体如何编译如何链接请参考 linux 操作记录 6.
7、程序 A 给 程序 B 发送信号,控制程序 B 在屏幕输出。
捕捉信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int isPrint = 0;
void catch_signal(int type)
{
switch(type)
{
case SIGINT:
printf("SIGINIT SIGNAL\n");
isPrint ^= 1;
break;
}
}
int customSignal(int signo, void(*func)(int))//固定格式
{
struct sigaction act, oldact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
return sigaction(signo, &act, &oldact);
}
int main(int arg, char* args[])
{
printf("Kill Process type: %d %d\n",SIGSTOP,SIGKILL);
customSignal(SIGINT, catch_signal);
int count = 0;
while(1)
{
if(isPrint)
{
printf("hello %d\n",count);
sleep(1);
count++;
}
}
return 0;
}
发送信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
int main(int arg, char* args[])
{
if(arg < 1) return -1;
int pid = atoi(args[1]);
kill(pid, SIGINT);
//int raise(int signal) == kill(getpid(), signal);
//alarm(unsigned int seconds); 经过seconds秒发送 SIGALRM 信号
return 0;
}
8、写一个守护进程,其一秒中向一个本地日志里面写一些内容。
错误案例!!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
/***
错误写法!!
***/
void initDaemon()
{
setsid();//提升为进程组长,脱离之前的控制终端
/*chdir("/");//改变工作目录,避免造成其它目录无法卸载
umask(0);//更改操作
//关闭不必要的文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);*/
}
void writeLog(const struct tm* pm, char* buf)
{
if(pm == NULL || buf == NULL)
{
printf("tm or buf is NULL...");
return;
}
/*
pm->tm_year + 1900;//年
pm->tm_mon + 1;//月
pm->tm_mday;//日
pm->tm_hour;//小时
pm->tm_min;//分钟
pm->tm_sec;//秒
*/
memset((char*)buf, 0, sizeof(buf));
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", (pm->tm_year + 1900),(pm->tm_mon + 1),(pm->tm_mday),(pm->tm_hour),(pm->tm_min),(pm->tm_sec));
}
int main(int arg, char* args[])
{
pid_t pid = fork();
if(pid > 0)
{
exit(0);
}
if(pid == 0)
{
printf("Child are created...");//printf 同时会打印到 mylog.txt中 为什么?
initDaemon();
FILE *f = fopen("mylog.txt","a");
if(f == NULL)
{
printf("Open file failed!");
return -1;
}
time_t t;
struct tm *p = NULL;
char buf[100];
int count = 0;
while(1)
{
printf("loop write %d...", count++);//printf 会打印到 mylog.txt中 为什么?
t = time(NULL);
p = gmtime(&t);
writeLog(p, buf);
fwrite(buf,strlen(buf),1,f);//一直到缓冲区写满才会写入
sleep(1);
}
fclose(f);
}
return 0;
}
正确写法
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void initDaemon()
{
setsid();//提升为进程组长,脱离之前的控制终端
/*一下两步 chdir umask 涉及到权限问题,在读写文件时会造成麻烦,先去掉*/
/*chdir("/");//改变工作目录,避免造成其它目录无法卸载
umask(0600);//更改操作*/
//关闭不必要的文件描述符 ,关闭后printf失效,不会再在屏幕输出
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
void writeLog(const struct tm* pm, char* buf)
{
if(pm == NULL || buf == NULL)
{
printf("tm or buf is NULL...\n");
return;
}
/*
pm->tm_year + 1900;//年
pm->tm_mon + 1;//月
pm->tm_mday;//日
pm->tm_hour;//小时
pm->tm_min;//分钟
pm->tm_sec;//秒
*/
memset((char*)buf, 0, sizeof(buf));
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d\n", (pm->tm_year + 1900),(pm->tm_mon + 1),(pm->tm_mday),(pm->tm_hour),(pm->tm_min),(pm->tm_sec));
}
int main(int arg, char* args[])
{
pid_t pid = fork();
if(pid > 0)
{
exit(0);
}
if(pid == 0)
{
printf("Child are created...\n");
initDaemon();
int fd = open("mylog.txt",O_APPEND | O_WRONLY);
if(fd == -1)
{
printf("Open file failed! %s \n", strerror(errno));
return -1;
}
printf("Open file Success! %d \n", fd);
time_t t;
struct tm *p = NULL;
char buf[100];
int count = 100;
while(count > 0)
{
count--;
//printf("loop write %d...\n", count++);
t = time(NULL);
p = gmtime(&t);
writeLog(p, buf);
printf("%s",buf);
write(fd, buf, strlen(buf));
sleep(1);
}
close(fd);
}
return 0;
}
9、编写一个程序,使用有名管道和多线程,让两个进程可以互相聊天。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//初始化了一个锁
/**
传给多线程参数的结构
**/
typedef struct ThreadArg
{
int* fd;//文件描述符
char* path;//文件路径
}TA;
/**
打开文件方式
同一个程序 既要打开一个只读FIFO 也要打开一个只写FIFO
用于互相传输
**/
int openFile(int * fd, const char* path, const char type)
{
switch(type)
{
case 'r':
*fd = open(path, O_RDONLY);
break;
case 'w':
*fd = open(path, O_WRONLY);
break;
default:
*fd = -1;
break;
}
if(*fd == -1)
{
printf("Open File Error ! %s\n",strerror(errno));
return -1;
}
printf("Open File Success ! %d\n", *fd);
return 0;
}
/**
读文件
**/
int readFile(int *fd)
{
//pthread_mutex_lock(&mutex);//加锁
printf("Start read file ! %d\n", *fd);
char buf[100] = {0};
while(1)
{
while(read(*fd, buf, sizeof(buf)) > 0)//循环读入数据到buf中
{
printf("%s",buf);//打印数据到屏幕
memset(buf,0,sizeof(buf));
}
}
//pthread_mutex_unlock(&mutex);//解锁
return 0;
}
/**
写文件
**/
int writeFile(int *fd)
{
printf("Start write file ! %d\n", *fd);
char buf[1024] = {0};//最多读入1024个字节
while(1)
{
//读取屏幕输入的字符串 不能读满,以免把'\0'给冲掉
read(STDIN_FILENO,buf, sizeof(buf) - 1);
write(*fd, buf, strlen(buf));//注意这里是strlen,有多少读多少
memset(buf, 0, sizeof(buf));
}
return 0;
}
void* readThreadFunc(void* arg)
{
TA* rt = arg;
openFile(rt->fd,rt->path,'r');//第一个文件用来读
readFile(rt->fd);
close(*(rt->fd));
return NULL;
}
void* writeThreadFunc(void* arg)
{
TA* wt = arg;
openFile(wt->fd,wt->path,'w');//第二个文件用来写
writeFile(wt->fd);
close(*(wt->fd));
return NULL;
}
int main(int arg, char* args[])
{
if(arg < 3) return -1;
int rfd, wfd;
TA rt, wt;
rt.fd = &rfd;
wt.fd = &wfd;
rt.path = args[1];
wt.path = args[2];
pthread_t readThread, writeThread;
//pthread_attr_t attr;
//pthread_attr_init(&attr);
//pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置可分离线程
//pthread_create(&readThread, &attr, readThreadFunc, &rt);
pthread_create(&readThread, NULL, readThreadFunc, &rt);
pthread_create(&writeThread,NULL, writeThreadFunc, &wt);
//pthread_attr_destroy(&attr);
/*
pthread_detach(readThread);//将此线程编程可分离线程
pthread_detach(writeThread);
*/
//等待子线程退出
pthread_join(readThread,NULL);
pthread_join(writeThread,NULL);
return 0;
}
10、TCP编写一个服务端和客户端,并可以实现服务端和客户端互相聊天
server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr_in SOCKADDR;
typedef struct SOCK_THREAD
{
int* client_sock;
pthread_t* thread_cancel;
}SOCK_THREAD;
void* recvThread(void* arg)
{
SOCK_THREAD sock_thread = *(SOCK_THREAD *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
int retrecv = recv(*(sock_thread.client_sock), buf, sizeof(buf), 0);
if(retrecv == -1)
{
printf("socket recv failed ! %s \n", strerror(errno));
break;
}
else if(retrecv == 0)
{
printf("socket closed %s\n", strerror(errno));
break;
}
else
{
printf("recv is %s", buf);
}
}
pthread_cancel(*(sock_thread.thread_cancel));
return NULL;
}
void* sendThread(void* arg)
{
SOCK_THREAD sock_thread = *(SOCK_THREAD *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));
if(send(*(sock_thread.client_sock), buf,strlen(buf),0) == -1)
{
printf("socket send failed ! %s \n", strerror(errno));
break;
}
}
pthread_cancel(*(sock_thread.thread_cancel));
return NULL;
}
int main(void)
{
//1, create socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
printf("socket create failed ! %s \n", strerror(errno));
return -1;
}
//参考操作记录 8
int opt = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(opt)) == -1)
{
printf("setsockopt failed ! %s \n", strerror(errno));
return -1;
}
//2, bind
SOCKADDR addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
printf("socket bind failed ! %s \n", strerror(errno));
return -1;
}
//3, listen
if(listen(sockfd, 20) == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
return -1;
}
//4, accept
while(1)
{
SOCKADDR cleint_addr;
socklen_t client_socklen = sizeof(cleint_addr);
int client_sockfd = accept(sockfd, (struct sockaddr*)&cleint_addr, &client_socklen);
if(client_sockfd == -1)
{
printf("socket accept failed ! %s \n", strerror(errno));
return -1;
}
printf("IP from %s \n", inet_ntoa(cleint_addr.sin_addr));
//5, send/recv
pthread_t recv_t, send_t;
SOCK_THREAD sd_thread1, sd_thread2;
sd_thread1.client_sock = &client_sockfd;
sd_thread1.thread_cancel = &recv_t;
sd_thread2.client_sock = &client_sockfd;
sd_thread2.thread_cancel = &send_t;
pthread_create(&recv_t, NULL, recvThread, &sd_thread1);
pthread_detach(recv_t);
pthread_create(&send_t, NULL, sendThread, &sd_thread2);
pthread_detach(send_t);
//此处不能join 否则线程会在此处挂起,无法接收下一个IP,造成只能一个client连接进来
/*pthread_join(recv_t, NULL);
pthread_join(send_t, NULL);*/
}
/*
char buffr[1024];
while(1)
{
memset(buffr, 0, sizeof(buffr));
int retrecv = recv(client_sockfd, buffr, sizeof(buffr), 0);
if(retrecv == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
close(client_sockfd);
return -1;
}
else if(retrecv == 0)
{
printf("socket closed %s\n", strerror(errno));
close(client_sockfd);
return 0;
}
else
{
printf("recv is %s \n", buffr);
memset(buffr, 0, sizeof(buffr));
read(STDIN_FILENO, buffr, sizeof(buffr));
if(send(client_sockfd, buffr,strlen(buffr),0) == -1)
{
printf("socket send failed ! %s \n", strerror(errno));
return -1;
}
}
}
*/
close(sockfd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr_in SOCKADDR;
typedef struct SOCK_THREAD
{
int* client_sock;//sock描述符
pthread_t* thread_cancel;//需要退出的线程
}SOCK_THREAD;
//接收消息线程
void* recvThread(void* arg)
{
SOCK_THREAD sock_thread = *(SOCK_THREAD *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
int retrecv = recv(*(sock_thread.client_sock), buf, sizeof(buf), 0);
if(retrecv == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
break;
}
else if(retrecv == 0)//正常退出
{
printf("socket closed %s\n", strerror(errno));
break;
}
else
{
printf("recv is %s", buf);
}
}
pthread_cancel(*(sock_thread.thread_cancel));
return NULL;
}
void* sendThread(void* arg)
{
SOCK_THREAD sock_thread = *(SOCK_THREAD *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));//read 会将线程挂起 虽然recvThread已经cancel 如果有join 它,还是会要输入read才能结束
if(send(*(sock_thread.client_sock), buf,strlen(buf),0) == -1)
{
printf("socket send failed ! %s \n", strerror(errno));
break;
}
}
pthread_cancel(*(sock_thread.thread_cancel));
return NULL;
}
int main(void)
{
//1, create socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
printf("socket create failed ! %s \n", strerror(errno));
return -1;
}
//2, connect
SOCKADDR addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = inet_addr("192.168.0.254");
if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
printf("socket connect failed ! %s \n", strerror(errno));
return -1;
}
//3, send recv msg
pthread_t recv_t, send_t;
SOCK_THREAD sd_thread1, sd_thread2;
sd_thread1.client_sock = &sockfd;
sd_thread1.thread_cancel = &recv_t;
sd_thread2.client_sock = &sockfd;
sd_thread2.thread_cancel = &send_t;
pthread_create(&recv_t, NULL, recvThread, &sd_thread1);
pthread_create(&send_t, NULL, sendThread, &sd_thread2);
pthread_join(recv_t, NULL);
// pthread_join(send_t, NULL);//read会阻塞所以不能join
/*
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));
if(send(sockfd, buf,strlen(buf),0) == -1)
{
printf("socket send failed ! %s \n", strerror(errno));
return -1;
}
memset(buf, 0, sizeof(buf));
int retrecv = recv(sockfd, buf, sizeof(buf), 0);
if(retrecv == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
close(sockfd);
return -1;
}
else if(retrecv == 0)
{
printf("socket closed %s\n", strerror(errno));
}
else
{
printf("recv is %s \n", buf);
}
}
*/
close(sockfd);
return 0;
}
makefile
.SUFFIXES:.c .o
CC=gcc
CFLAGS=-Wall -g
SRCS1=client.c
SRCS2=server.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=client
EXEC2=server
start:$(OBJS1) $(OBJS2)
$(CC) -o $(EXEC1) $(OBJS1) -lpthread
$(CC) -o $(EXEC2) $(OBJS2) -lpthread
@echo "----Build Success----"
.c.o:
$(CC) $(CFLAGS) -o $@ -c $<
clean:
rm -f *.o
-rm core.*
clexe:
rm $(EXEC)
11、链接数据库,并可以执行查询语句
#include <stdio.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <string.h>
#include <unistd.h>
int main(int argsc, char*argsv[])
{
printf("MySql Connectting....\n");
//----链接数据库-----
MYSQL mysql, *connection;
//1, 初始化
mysql_init(&mysql);//mysql socket 初始化
//2, 链接数据库
//连接到数据库,mysql_real_connect返回链接标识符
//connection 应该就是 mysql 的地址, 因为在下面的操作中两者可以替换
connection = mysql_real_connect(&mysql, "localhost","liju","123456","db1",0,0,0);
//如果为空直接返回
if(connection == NULL)
{
printf("MySql Connect Error!! %s\n", mysql_error(&mysql));
return EXIT_FAILURE;
}else{
printf("MySql Connectted!!\n");
}
//3,发送sql语句
/*
mysql_query() cannot be used for statements that contain binary data;
you must use mysql_real_query() instead.
(Binary data may contain the “\0” character,
which mysql_query() interprets as the end of the statement string.)
In addition, mysql_real_query() is faster than mysql_query()
because it does not call strlen() on the statement string.
//the last param means to query lenth, you can use it like this strlen(query)
int mysql_real_query(MYSQL *mysql, const char *query, unsigned int length)
mysql_query((MYSQL *mysql, const char *query)
*/
int ret = 0;
ret = mysql_query(connection, "set names utf8");//先设置字符集,避免字符不匹配而查询不了。
//成功返回 0
if(ret == 0)
{
printf("set names utf8 \n");
}else{
printf("set names utf8 failure!! %s\n", mysql_error(connection));
}
char promp[1024] = {0};
sprintf(promp, "%s", "insert name>");
write(STDOUT_FILENO, promp, strlen(promp));//如果是用 printf 会被 read 阻塞住,造成只有在 read 执行之后才打印
char name[1024];
memset(name, 0, sizeof(name));
read(STDIN_FILENO, name, sizeof(name));
name[strlen(name) - 1] = 0;//去掉回车
//输入执行语句
char sql[1024];
memset(sql, 0, sizeof(sql));
sprintf(sql, "select name from table1 where name = '%s'", name);
printf("%s\n", sql);
ret = mysql_query(connection, sql);
//成功返回 0
if(ret == 0)
{
printf("Search Success!!\n");
}else{
printf("Search failure!! %s\n", mysql_error(connection));
}
/*ret = mysql_query(connection, "select name from table1 where name = '季欣'");
//成功返回 0
if(ret == 0)
{
printf("Search Success!!\n");
}else{
printf("Search failure!! %s\n", mysql_error(connection));
}*/
//4, 关闭数据库
//关闭链接标识符
mysql_close(connection);
//-------------------
return EXIT_SUCCESS;
}
12、用C语言编写一个程序,执行数据库语句,并且能打印结果到屏幕上,解决退格键显示^H 问题。
#include <stdio.h>
#include <stdlib.h>
#include <mysql/mysql.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
struct termios oldterm;
/*
tcgetattr用于获取终端的相关参数,成功返回零;失败返回非零;而tcsetattr函数用于设置终端参数,成功返回零;失败返回-1;
发生失败接口将设置errno错误标识。
返回的结果保存在termios结构体中,该结构体一般包括如下的成员:
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
VINTR 代表 ctrl+c 中断信号
term.c_cc[VINTR] = '\t'; 代表将tab设置成中断信号
具体请见博客内容
http://blog.csdn.net/kakaxi2222/article/details/45269627
*/
//ctrl+s 锁屏 ctrl+q 解锁屏
//用代码来控制退格键不回显
void setstty()
{
struct termios term;
if(tcgetattr(STDIN_FILENO, &term) == -1)//得到当前系统的 termios 设置
{
printf("tcgetattr error is %s \n", strerror(errno));
return;
}
oldterm = term;//先保存之前的设置以供将来可以恢复设置
term.c_cc[VERASE] = '\b';
if(tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1)
{
printf("tcsetattr error is %s \n", strerror(errno));
return;
}
}
//恢复系统的termios设置
void returnstty()
{
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &oldterm) == -1)//设置系统termion
{
printf("tcsetattr error is %s\n", strerror(errno));
}
return;
}
int main(int argsc, char*argsv[])
{
//不回显退格建系统命令(当输入中文时有bug, 会多删除字符)
//system("stty erase ^H");
//不回显代码控制(当输入中文时有bug, 会多删除字符)
setstty();
printf("MySql Connectting....\n");
//----链接数据库-----
MYSQL mysql, *connection;
//1, 初始化
mysql_init(&mysql);//mysql socket 初始化
//2, 链接数据库
//连接到数据库,mysql_real_connect返回链接标识符
//connection 应该就是 mysql 的地址, 因为在下面的操作中两者可以替换
connection = mysql_real_connect(&mysql, "localhost","liju","123456","db1",0,0,0);
//如果为空直接返回
if(connection == NULL)
{
printf("MySql Connect Error!! %s\n", mysql_error(&mysql));
return EXIT_FAILURE;
}else{
printf("MySql Connectted!!\n");
}
//3,发送sql语句
/*
mysql_query() cannot be used for statements that contain binary data;
you must use mysql_real_query() instead.
(Binary data may contain the “\0” character,
which mysql_query() interprets as the end of the statement string.)
In addition, mysql_real_query() is faster than mysql_query()
because it does not call strlen() on the statement string.
//the last param means to query lenth, you can use it like this strlen(query)
int mysql_real_query(MYSQL *mysql, const char *query, unsigned int length)
mysql_query((MYSQL *mysql, const char *query)
*/
int ret = 0;
ret = mysql_query(connection, "set names utf8");//先设置字符集,避免字符不匹配而查询不了。
//成功返回 0
if(ret == 0)
{
printf("set names utf8 \n");
}else{
printf("set names utf8 failure!! %s\n", mysql_error(connection));
}
char promp[1024] = {0};
sprintf(promp, "%s", "insert name>");
write(STDOUT_FILENO, promp, strlen(promp));//如果是用 printf 会被 read 阻塞住,造成只有在 read 执行之后才打印
char name[1024];
memset(name, 0, sizeof(name));
read(STDIN_FILENO, name, sizeof(name));
name[strlen(name) - 1] = 0;//去掉回车
//输入执行语句
char sql[1024];
memset(sql, 0, sizeof(sql));
//sprintf(sql, "select name from table1 where name = '%s'", name);
sprintf(sql, "delete from table1 where name = '巫文超'");
printf("%s\n", sql);
ret = mysql_query(connection, sql);
//成功返回 0
if(ret == 0)
{
//%u mysql_affected_rows(connection)
//mysql_store_result 返回查询结果 并保存到 result中
MYSQL_RES *result = mysql_store_result(connection);
if(result == NULL)
{
printf("resulst is null \n");
return 0;
}
//记录列
MYSQL_FIELD *field;
//返回有多少字段(列)
while((field = mysql_fetch_field(result)) != NULL)
{
printf("%s\t", field->name);
}
printf("\n");
//记录行
MYSQL_ROW row;
int count = 0;
while(1)
{
//提取每一条记录,返回行
row = mysql_fetch_row(result);
if(row == NULL) break;
printf("%d ", count++);
}
//一定要用这个函数将result free掉
mysql_free_result(result);
printf("Search Success!!\n");
}else{
printf("Search failure!! %s\n", mysql_error(connection));
}
/*ret = mysql_query(connection, "select name from table1 where name = '季欣'");
//成功返回 0
if(ret == 0)
{
printf("Search Success!!\n");
}else{
printf("Search failure!! %s\n", mysql_error(connection));
}*/
//4, 关闭数据库
//关闭链接标识符
mysql_close(connection);
//-------------------
returnstty();
return EXIT_SUCCESS;
}
13、模仿 mysql client 编写一个 自己的mysql client
mydb.h
#ifndef MYDB_H
#define MYDB_H
#include <mysql/mysql.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
//初始化 mysql 对象
int initMySQL(MYSQL* mysql);
//链接 mysql server
int connectMySQL(MYSQL* mysql, const char* host, const char* userName, const char* password, const char* dbName);
//生成sql语句
int sendAndResponseMySQL(MYSQL* mysql, const char* sql);
//关闭数据库链接
int closeMySQL(MYSQL* mysql);
#endif
mydb.c
#include "mydb.h"
int initMySQL(MYSQL* mysql)
{
if(mysql == NULL)
{
printf("MYSQL is NULL !\n");
return -1;
}
mysql_init(mysql);
return 0;
}
int connectMySQL(MYSQL* mysql, const char* host, const char* userName, const char* password, const char* dbName)
{
if(mysql_real_connect(mysql, host, userName, password, dbName, 0, 0, 0) == NULL)
{
printf("%s\n", mysql_error(mysql));
return EXIT_FAILURE;
}
return 0;
}
int sendAndResponseMySQL(MYSQL* mysql, const char* sql)
{
if(mysql == NULL)
{
printf("MYSQL is NULL !\n");
return -1;
}
int ret = mysql_query(mysql, sql);
if(ret != 0)
{
printf("%s\n", mysql_error(mysql));
return -1;
}
MYSQL_RES *result = mysql_store_result(mysql);
if(result == NULL)
{
printf("Query OK, %u rows affected\n", (unsigned int)mysql_affected_rows(mysql));
return 0;
}
MYSQL_FIELD *field;
int cols = 0;
while((field = mysql_fetch_field(result)) != NULL)
{
printf("%s\t", field->name);
cols++;
}
printf("\n");
MYSQL_ROW row;
while((row = mysql_fetch_row(result)) != NULL)
{
int i = 0;
for(; i < cols; i++)
{
printf("%s\t", row[i]);
}
printf("\n");
}
printf("Query OK, %u rows affected\n", (unsigned int)mysql_affected_rows(mysql));
mysql_free_result(result);
return 0;
}
int closeMySQL(MYSQL* mysql)
{
if(mysql == NULL)
{
printf("MYSQL is NULL !\n");
return -1;
}
mysql_close(mysql);
printf("Bye\n");
return 0;
}
mysqlclient.c
#include "mydb.h"
int main(int argsc, char* argsv[])
{
if(argsc != 4)
{
return EXIT_FAILURE;
}
if(strncmp(argsv[1], "-u", 2) != 0)
{
return EXIT_FAILURE;
}
if(strncmp(argsv[3], "-p", 2) != 0)
{
return EXIT_FAILURE;
}
const char* password = getpass("Please input password:");
write(STDOUT_FILENO, "Please input DB name:", strlen("Please input DB name:"));
char dbName[100] = {0};
read(STDIN_FILENO, dbName, sizeof(dbName));
dbName[strlen(dbName) - 1] = 0;
system("stty erase ^H");
MYSQL mysql;
initMySQL(&mysql);
if(connectMySQL(&mysql, "localhost", argsv[2], password, dbName) !=0 )
{
return EXIT_FAILURE;
}
char sql[1024];
while(1)
{
write(STDOUT_FILENO, "mysql> ", strlen("mysql> "));
memset(sql, 0, sizeof(sql));
read(STDIN_FILENO, sql, sizeof(sql));
sql[strlen(sql) - 1] = 0;
if(strncmp(sql, "quit", 4) == 0)
{
break;
}
sendAndResponseMySQL(&mysql, sql);
}
closeMySQL(&mysql);
return EXIT_SUCCESS;
}
14、用epoll来处理大并发问题。(模板)
/**
epoll 有两种模式 ET(高速工作方式 只支持noblok socket) LT
ET:: edge-trigger 模式下只有某个 fd 从 unreadable 变为 readable
或从 unwritable 变为 writable 时,epoll_wait 才会返回该 fd。也就是说
如果一个fd的状态在没有改变的情况下,如果来了同样是事件,那么ET 就不会处理这个事件??
LT:: LT 模式是缺省模式,当epoll正在处理消息时, 又来了一个消息,
则下一次循环时,会立即返回这个消息,以供处理。LT 不会丢失消息。
只要buffer有数据,就不断的通知。 level-trigger 模式下只要某个 fd 处于
readable/writable 状态,无论什么时候进行 epoll_wait 都会返回该 fd
epoll 模型适用于 数量较多 但是 数据量较少的连接,因为它要不断的轮询。
多线程 模型适用于 数量较少 但是 数据量较大的情况。
**/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define EPOLL_NUMS 100
typedef struct sockaddr_in SOCKADDR;
int acceptThread(int fd)
{
SOCKADDR cleint_addr;
socklen_t client_socklen = sizeof(cleint_addr);
int client_sockfd = accept(fd, (struct sockaddr*)&cleint_addr, &client_socklen);
if(client_sockfd == -1)
{
printf("socket accept failed ! %s \n", strerror(errno));
}
return client_sockfd;
}
//接收消息
int recvThread(int fd)
{
char buf[1024];
int ret = 0;
while(1)
{
memset(buf, 0, sizeof(buf));
int retrecv = recv(fd, buf, sizeof(buf), 0);
if(retrecv == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
ret = - 1;
break;
}
else if(retrecv == 0)//正常退出
{
printf("socket closed %s\n", strerror(errno));
ret = -1;
break;
}
else
{
printf("recv is %s", buf);
}
}
return ret;
}
//发送消息
int sendThread(int fd)
{
char buf[1024];
int ret = -1;
while(1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));//read 会将线程挂起 虽然recvThread已经cancel 如果有join 它,还是会要输入read才能结束
if(send(fd, buf,strlen(buf),0) == -1)
{
printf("socket send failed ! %s \n", strerror(errno));
ret = -1;
break;
}
}
return ret;
}
//创建非阻塞
void setnonblocking(int fd)
{
int opts = fcntl(fd, F_GETFL);
if(opts < 0)
{
printf("fcntl failed %s\n", strerror(errno));
}
opts = opts | O_NONBLOCK;
if(fcntl(fd, F_SETFL, opts) < 0)
{
printf("fcntl failed %s\n", strerror(errno));
}
}
//先创建服务端的socket
int socket_create()
{
//1,创建SOCKET
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
printf("socket create failed ! %s \n", strerror(errno));
return -1;
}
//2, 断开再连接无需等待,具体查看博客 操作记录 8
int opt = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
{
printf("setsockopt failed ! %s \n", strerror(errno));
return -1;
}
//3, 绑定端口和IP
SOCKADDR addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
printf("socket bind failed ! %s \n", strerror(errno));
return -1;
}
//4, 开始侦听
if(listen(sockfd, 20) == -1)
{
printf("socket listen failed ! %s \n", strerror(errno));
return -1;
}
return sockfd;
}
int main(void)
{
//1、获得服务端的 socket 描述符
int sockfd = socket_create();
//2、设置为非阻塞
setnonblocking(sockfd);
//3、声明一个 epll_event 结构,用来放入 epoll 池中,每次轮询都会从池中加入有变化的 描述符
struct epoll_event ev, events[EPOLL_NUMS];
//4、创建epoll的描述符 数量和池的数量一样大, 表示对多能放多少文件描述符
int epfd = epoll_create(EPOLL_NUMS);
//5、把服务端 fd 加到epoll池中
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP ;//| EPOLLET;
//用来添加、修改、删除需要侦听的文件描述符及其事件
//每次添加、修改、删除文件描述符都要用到epoll_ctr,所以
//要尽量少的调用epoll_ctl.
//EPOLL_CTL_ADD 添加fd到 epfd中
//EPOLL_CTL_MOD 修改指定的fd
//EPOLL_CTL_DEL 从epfd中删除一个fd
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
int st = -1;
//6、开始轮询
while(1)
{
//返回有事件的描述符的总数量
int fd_num = epoll_wait(epfd, events, EPOLL_NUMS, -1);
//错误返回-1
if(fd_num == -1)
{
printf("%s\n", strerror(errno));
break;
}
//遍历每个有事件的描述符, 这时 events 里面存的只有来事件的描述符
int i;
for( i = 0; i < fd_num; i++)
{
if(events[i].data.fd < 0)
{
continue;
}
//假如是服务端的描述符则执行accept, 并且将客户端的 描述符 添加到池中
if(events[i].data.fd == sockfd)
{
st = acceptThread(sockfd);
if(st >= 0)
{
ev.data.fd = st;
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
epoll_ctl(epfd, EPOLL_CTL_ADD, st, &ev);
continue;
}
}
//如果来的是输入事件,则执行socket_recv
if(events[i].events & EPOLLIN)
{
st = events[i].data.fd;
if(recvThread(st) <= 0)
{
close(st);
events[i].data.fd = -1;
}
}
//如果是错误事件,则执行 close
if(events[i].events & EPOLLERR)
{
close(st);
events[i].data.fd = -1;
}
//如果是挂起事件, 则执行close
if(events[i].events & EPOLLHUP)
{
close(st);
events[i].data.fd = -1;
}
}
//7、循环结束关闭epoll ,epoll 描述符用完后需要用close关闭
close(epfd);
return 0;
}
}