c语言获取今日,明日,昨日的方法

Posted: 2010年6月7日星期一 –
最近要处理大量的log,基本上按天保存log文件,这就牵涉到日期问题,本来采用shell写了一套程序,发现效率不是很高,所以改用c语言重新写了一份,下面是获取系统的今日日期和昨日的一个demo

#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
time_t t;
struct tm *m;

t = time(NULL) - 24 * 3600;
m = localtime(&t);
printf("Yesterday is: %d%.2d%.2d\n", m->tm_year + 1900, m->tm_mon + 1, m->tm_mday);

t = time(NULL);
m = localtime(&t);
printf("Today is: %d%.2d%.2d\n", m->tm_year + 1900, m->tm_mon + 1, m->tm_mday);


return 0;
}

nginx论坛防刷贴的实例

Posted: 2010年6月6日星期日 –
最近我的一个朋友跟我抱怨他的论坛被一群无聊的人在刷帖,问我有什么好的方法避免这种情况的发生,由于他使用的web服务器是nginx,我就利用web站点的访问log和nginx的目录访问权限规则写了个脚本,实现上面的功能,具体脚本的设计如下:
每5分钟探测当前这一个小时单IP的访问量,如果达到设置的上限,直接返回403错误页面给他,禁止他在接下来的一个小时的访问,并记录下他的ip,若在接下来的一个小时访问量正常,再智能的把他的站点的访问权限放开,允许他继续使用论坛,这样就拒绝了这群无聊的人,当然记得排除自己的ip地址。
附shell脚本实例

#!/bin/sh

LANG=UTF-8
onehourago=`date -d "1 hour ago" +%d\/%b\/%Y:%H`
now=`date +%d\/%b\/%Y:%H`
grep -v 1.1.1.1 /usr/local/nginx/logs/access.log | awk '/$onehourago/{++S[$1]}END{for (a in S) print a, S[a]}' | awk '{if($2>5000) print }' > /tmp/onehoureagodenyip
grep -v 1.1.1.1 /usr/local/nginx/logs/access.log | awk '/$now/{++S[$1]}END{for (a in S) print a, S[a]}' |awk '{if($2>5000) print }' > /tmp/nowdenyip
cat /tmp/onehoureagodenyip >> /tmp/nowdenyip
awk '{print $1}' /tmp/nowdenyip | sort |uniq > /tmp/denyip
cat /dev/null > /usr/local/nginx/conf/acl.conf
if [ ! -z /tmp/denyip ] ; then
for i in `cat /tmp/denyip`
do
echo "deny $i;" >> /usr/local/nginx/conf/acl.conf
done
fi
echo "allow all;" >> /usr/local/nginx/conf/acl.conf
echo "location ~ .*\.php?$ {
include /usr/local/nginx/conf/enable_php5.conf;
}" >> /usr/local/nginx/conf/acl.conf
kill -HUP `ps -ef |grep "nginx: master" |grep -v grep |awk '{print $2}'`

脚本说明:
先在nginx的主配置文件中server断中加入如下的内容:
location ^~ / {
include /usr/local/nginx/conf/acl.conf;
}
排除1.1.1.1的ip,同时当单IP一个小时的访问量达到5000次就屏蔽它(要是你的log中记录的东西不多的话,90%的用户单IP访问量一小时不可能有这么大)。

实时监控demo

Posted: 2010年5月22日星期六 –
最近写了一个实时服务器资源监控系统的demo,具体URL详见:http://60.29.244.76/live/live.html
目前能监控cpu,内存,load,网络连接状态,带宽。每2秒刷新一次,每条线都有开关,实现在动态显示,
Y轴随关闭的线,动态显示大小,每个点都有显示当前的实时状态的点,鼠标移动到相应的点能显示出来,如果闲更新太快,显示看不清楚,可以点击暂停,等你看清楚了可再点击开启实现再监控的功能。
目前只是一个demo程序,很多东西还没有,后期完善后,准备开源。

c实现数据字典批量替换文本内容

Posted: 2010年3月7日星期日 –
最近工作中需要大量替换log日志中的IP地址,使之显示ip对应的地区信息,于是用C写了个程序实现上述功能。
具体代码如下:

#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU //为使用strcasestr函数添加
#include <string.h>

void str_replace(char *buf, const char *pre, const char *aft)
{
char *p;
//不区分大小写
p = strcasestr(buf, pre);
if (p){
char *p_2 = p + strlen(pre);
str_replace(p_2, pre, aft);
memmove(p + strlen(aft), p_2, strlen(p_2)+1);
memcpy(p, aft, strlen(aft));
}
}

int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char buf1[1024] = {0},
buf2[1024] = {0},
id[20] = {0},
name[32] = {0};

if (argc != 3) {
printf("%s \n", argv[0]);
exit(-1);
}

fp1 = fopen(argv[1], "r");
if (fp1 == NULL) {
printf("Open file %s FAILE!", argv[1]);
exit(-1);
}

fp2 = fopen(argv[2], "r");
if (fp2 == NULL) {
printf("Open file %s FAILE!", argv[2]);
exit(-1);
}

while (fgets(buf1, sizeof(buf1), fp1)) {
fseek(fp2, 0, SEEK_SET);
while (fgets(buf2, sizeof(buf2), fp2)) {
sscanf(buf2, "%s %s", id, name);
str_replace(buf1, id, name);
}
printf("%s", buf1);
}

fclose(fp1);
fclose(fp2);

return 0;
}

编译:
gcc -o replace replace.c

使用:
./replace apache.log ip.txt
其中apahce.log文件可以为任何格式的文本文件,ip.txt文件具体格式如下所示:

1.1.1.1 北京电信
2.2.2.2 天津联通
3.3.3.3 广州移动

第一列为IP地址,第二列为ip地址对应的地区信息。
该工具同样适用于其它的类似替换,代码中已做了不区分大小写处理。

shell编程中文本处理中用到的几张图

Posted: 2009年11月10日星期二 –
在网上找到几个shell文本处理的几个常用命令的使用方法,觉得比较好,就贴在博客中,权当备忘,由于在google图片搜索出来的,不知道原载作者是谁,如知道,告诉我一声,加上原作者地址,下面的图直接看比较小,可以直接点击图,看大图,或者下载下来作为文件保存
正则表达式:

vi使用

sed使用

awk使用

分析the address already in use产生的原因

Posted: 2009年11月5日星期四 –
最近在维护一个别人写的程序时遇到一个它的程序启动不了的情况,具体情况如下,服务器端按照它的方式不能正常关闭,就用kill命令把它的程序结束掉了,再启动程序时报如下的错误:the address already in use,问了公司的同事,同事也告诉不可能啊,要不就是由什么链入了系统库了,当时我也不懂也没有在意,没有去查,这几天我刚好在学习linux网络编程,也碰到了这个问题,通过查找资料,了解到,并不是什么链入了系统库了,而是下面将要说明的情况:
一般情况认为,server socket已经关闭,理应释放了ip address及port资源,但是事实并不一定是这样的,因为大部分socket连接是基于tcp协议多次握手连接的基础上,以保证数据传输的完整性,我们都知道tcp协议是一种可靠的协议,它有3次握手,四次挥手过程,下面是4次挥手过程
(1)当服务器端处于ESTABLISHED状态时, 服务器欲主动关闭连线,发送FIN至客户端,进入到FIN-WAIT-1状态,等待客户端回应ACK,表示等待确认客户端得知服务器端要关闭连线
(2)当客户端收到服务器端的FIN时,立即回应ACK给服务器端,进入到CLOSE-WAIT状态,表示须等到应用程序没有任何资料要传送给服务器端, 客户端才决定关闭连线
(3)服务器端收到客户端回应的ACK,表示确认客户端得知服务器要关闭连线,此刻等待客户端发送FIN
(4)客户端决定关闭连线,发送FIN至服务器端,进入到LAST-ACK状态,等待服务器端回应ACK,表示等待确认服务器端得知客户端要关闭连线
(5)服务器端收到客户端的FIN后,随即回应ACK,进入TIME_WAIT状态,表示服务器端得知客户端要关闭连线,且等待2MSL时间,以防客户端再次发送FIN
(6)客户端收到服务器端回应的ACK后,进入CLOSED状态,表示客户端已确认服务器端已得知它要关闭连线,才进行关闭连线
(7)服务器端等待2MSL时间,才进入CLOSED状态,关闭连线,并自连线表中移除
在上述的第(5),(7)点,此刻TIME-WAIT的用意在于,虽服务器已确认客户端要关闭连线,且回应了ACK给客户端,但不保证客户端会收到ACK ,一旦ACK遗漏,
客户端会再次发送FIN给服务器端,再次进行确认,所以服务器端须进入TIME-WAIT状态,等待2MSL时间,预防客户端会再次发送FIN,进行连线关闭的确认。

从上面的4次挥手过程得知,我们直接结束游戏的服务器端,它并没有马上释放它的ip及port资源就在于服务器端进入了time_wait状态,有可能客户端没有
收到ack资源,导致不停的发送fin请求,导致直接结束服务器端,导致重启起不来,下面是我用C写的一个程序怎么重现上面所出现的实例,以及怎么避免上面所
出现的情况
1.客户端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
struct sockaddr_in server;
int sock;
char buf[32];
int n;

sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(1);
}

server.sin_family = AF_INET;
server.sin_port = htons(1234);
server.sin_addr.s_addr = inet_addr("127.0.0.1");

if(connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0){
perror("connetc");
exit(1);
}

n = read(sock, buf, sizeof(buf));
if(n < 0) {
perror("read");
return 1;
}

printf("%d, %s\n", n, buf);

close(sock);

return 0;
}

帮上面的代码保存为一个client.c文件,通过下面命令进行编译
gcc -o client client.c
2.重现出现情况的服务器端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;

sock0 = socket(AF_INET, SOCK_STREAM, 0);
if (sock0 < 0) {
perror("socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("bind");
return 1;
}

if (listen(sock0, 5) !=0) {
perror("listen:");
return 1;
}
while (1) {
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
if (sock < 0) {
perror("accept:");
break;
}

printf("accepted connection from %s\n",
inet_ntoa(client.sin_addr));
if(send(sock, "Hello", 5, 0) < 1) {
perror("write:");
return 1;
}

close(sock);
}
close(sock0);

return 0;
}

把上面的程序保存为一个server1.c文件,执行下面的命令进行编译
gcc -o server1 server1.c
3.避免上述情况发生的服务器端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;
int yes = 1;

sock0 = socket(AF_INET, SOCK_STREAM, 0);
if (sock0 < 0) {
perror("socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
setsockopt(sock0,
SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
if(bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("bind");
return 1;
}

if (listen(sock0, 5) !=0) {
perror("listen:");
return 1;
}
while (1) {
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
if (sock < 0) {
perror("accept:");
break;
}

printf("accepted connection from %s\n",
inet_ntoa(client.sin_addr));
if(send(sock, "Hello", 5, 0) < 1) {
perror("write:");
return 1;
}

close(sock);
}
close(sock0);

return 0;
}

把上面的程序保存为一个server2.c文件,执行下面的命令进行编译
gcc -o server2 server2.c

启动服务器端
./server1
在另外一个终端中启动客户端
./clinet
得到如下的内容
5, Hello
在服务器的终端通过ctrl + c结束刚刚起来的服务器端,在一分钟内再启动服务器端,你就可以发现出现下面的错误信息
bind: Address already in use
过1分钟后(Linux系统默认time_wait的时间为60秒)就可以发现可以起来了。
为规避上面的情况,再启动server2,再重复上面的步骤,发现已经没有这种情况了。

上面只是一个具体的实例,并不保证数据的完整性,具体到它们的程序中,具体进行分析。

c开发的中的man使用技巧

Posted: 2009年11月2日星期一 –
在c语言开发过程中,经常需要进行查找函数的说明,在linux下有man命令,比较容易获取c的标准库函数的说明,在ubuntu下若要查看C语言的函数说明,需要安装manpages-dev包,具体安装方法如下
sudo apt-get install manpages-dev
若对于英文不好的同学,可以再安装一个中文的语言包:manpages-zh,这个语言包并不一定包含所有的函数说明,只是其中的一部分,安装方法如下:
sudo apt-get install manpages-zh
通常对于一些不常用的函数可以直接通过man 函数名,可以得到所需的函数说明,对于函数名跟系统命令相同时,就不会得到你想要的函数说明,具体怎么区分怎么才能得到了我想要的函数说明呢?
通常做法如下所示
1. man -k 函数名 |grep ^函数名
2.通过查到的结果,一般会在后面加一个括号带一个数字,再通过man 刚刚得到的数字 函数名 得到我们想要的函数帮助文档
譬如查看c语言read函数说明
man read |grep ^read
得到的结果如下:
zhou@lingjie:~$ man -k read |grep ^read
read (2) - read from a file descriptor
readahead (2) - perform file readahead into page cache
readahead-list (8) - manual page for readahead-list: 0.20050517.0220
readahead-watch (8) - manual page for readahead-watch: 0.20050517.0220
readdir (2) - read directory entry
readdir (3) - read a directory
readdir_r (3) - read a directory
readelf (1) - Displays information about ELF files.
readline (3readline) - get a line from a user with editing
readlink (1) - display value of a symbolic link
readlink (2) - read value of a symbolic link
readlinkat (2) - read value of a symbolic link relative to a directory ...
readom (1) - read or write data Compact Discs
readprofile (1) - a tool to read kernel profiling information
readv (2) - read or write data into multiple buffers

那么我查看C的函数说明就是如下的命令
man 2 read
就可以得到我们想要的函数说明了。