学习unix高级编程的线程方面的内容时,写了个简单的缓存服务器。
思路:服务监听在固定端口,有客户端连接过来时,创建新线程,把连接号给该线程,由该线程负责与客户端的交互。该线程一直服务于该客户端,除非对方发出了bye的命令,(或者超时,这边没做)。真正的内容保存和获取则由专门的memserver来实现(保存时没有加锁操作)。
交互的协议为:
服务器先读,客户端先写;
客户端等待读,服务器处理上一步获取的命令;
服务返回命令处理结果。
内容格式:
头三个字节:命令;
接着2个字节:整个包长度;
接着字节是内容;
接着1字节:key长度
接着字节是key内容
接着2字节:内容长度
接着是内容
服务端:——————————————————————
/** memserver.h **/
struct InterCont{
char* key;
char* content;
};
struct Meta{
char* key;
int keyLen;
int readCount;
int contLen;
struct InterCont* realContent;
struct Meta* next;
};
struct entry{
int count;
struct Meta* meta;
};
int save(const struct InterCont* val);
int del(const char* key);
struct InterCont* get(const char* key);
void printMemInfo();
void printInterCont(const struct InterCont* cont);
/** memserver.c **/
#include "memserver.h"
#include <stdio.h>
#define ENTRY_CNT 20
//entry for record
struct entry *memEntry[ENTRY_CNT];
//memory info
long entryPointMemInByte=0;
long metaMemInByte=0;
long contentMemInByte=0;
int hash(const char key[]){
char one;
int sum=0;
int i=0;
one=key[0];
printf("-------------\n%c",one);
i=1;
while(one!='\0'){
sum+=(int)one;
one=key[i++];
printf("%c",one);
}
printf("\nsum=%d\n----------------",sum);
return sum % ENTRY_CNT;
}
int del(const char* key){
printf("del key=%s\n",key);
}
int save(const struct InterCont* val){
printf("save one ....");
// printInterCont(val);
if(val==NULL){
return 0;
}
int hk=hash(val->key);
printf("save %s , hash key=%d\n",val->key,hk);
struct entry* et=memEntry[hk];
printf("prepare content memory...\n");
//alloc content
struct InterCont* newone=(struct InterCont*)malloc(sizeof(struct InterCont));
newone->key=val->key;
newone->content=val->content;
printf("prepare meta...\n");
//meta
struct Meta* meta=(struct Meta*)malloc(sizeof(struct Meta));
meta->key=val->key;
meta->keyLen=strlen(val->key);
meta->readCount=0;
meta->contLen=strlen(val->content);
meta->realContent=newone;
meta->next=NULL;
if(et!=NULL){
printf("hk=%d has alreay as least store one content!will extend its meta list !\n");
//find the last meta
struct Meta* inchain=et->meta;
//iterate until the last or encount same key
while(inchain->next!=NULL && strcmp(inchain->key,val->key)){
inchain=inchain->next;
};
//if key already exists
if(strcmp(inchain->key,val->key)==0){
printf("save key:%s already exist!will replace old value with the new one!\n",val->key);
free(inchain->realContent->content);
inchain->realContent->content=malloc(strlen(val->content)+1);
strcpy(inchain->realContent->content,val->content);
free(meta);
}
else{
inchain->next=meta;
et->count++;
}
}else{
//entry is empty ,so use it
printf("save , first use entry %d\n",hk);
et=(struct entry*)malloc(sizeof(struct entry));
memEntry[hk]=et;
et->count=1;
et->meta=meta;
//incre entry mem info
entryPointMemInByte+=sizeof(struct entry);
}
printf("finished save!\n");
//refresh memory info
return 1;
}
struct InterCont* get(const char* key){
int hk=hash(key);
struct entry* one= memEntry[hk];
if(one!=NULL){
int cnt=one->count;
if(cnt>1){
printf("hk=%d has more then one value:%d\n",hk,cnt);
}
struct Meta* meta=one->meta;
if(meta==NULL){
printf("error!no meta data found!hk=%d\n",hk);
return NULL;
}
struct InterCont* ret=NULL;
int keylen=strlen(key);
do{
if(meta->keyLen==keylen){
char* cmpkey=meta->key;
if(strcmp(cmpkey,key)==0){
ret=meta->realContent;
meta->readCount++;
return ret;
}
}
meta=meta->next;
}while(meta!=NULL);
}
return NULL;
}
int getMetaSize(struct Meta* meta){
int sum=0;
sum+=sizeof(struct Meta);
sum+=strlen(meta->key)+1;
return sum;
}
long getInterContSize(struct InterCont* cont){
long sum=0;
sum+=sizeof(struct InterCont);
sum+=strlen(cont->key)+1;
sum+=strlen(cont->content)+1;
return sum;
}
void printOneMeta(struct Meta* mt){
printf("--------------------print meta begin---------------\n");
printf("meta key=%s,readCount=%d\n",mt->key,mt->readCount);
printf("content=%s\n",mt->realContent->content);
printf("--------------------print meta end ---------------\n");
}
void printOneEntry(struct entry* ent){
printf("--------------print entry begin---------------\n");
printf("entry count=%d\n",ent->count);
struct Meta* mt=ent->meta;
while(mt!=NULL){
printOneMeta(mt);
mt=mt->next;
}
printf("--------------print entry end---------------\n");
}
void printMemInfo(){
printf("\n--------printMemInfo begin-------------------\n");
int i=0;
for(;i<ENTRY_CNT;i++){
if(memEntry[i]!=NULL){
printOneEntry(memEntry[i]);
}
}
printf("--------printMemInfo end -------------------\n");
}
void printInterCont(const struct InterCont* cont){
if(cont==NULL){
printf("empty cont!\n");
return;
}
else{
printf("InterCont info[key=%s,cont=%s\n",cont->key,cont->content);
}
}
//void main(){
/*
char key[]="ha";
key[sizeof(key)]='\0';
char* fp=key;
int hk=hash(key);
printf("key len=%d,hash value of key: \'%s\' is %d\n",strlen(key),key,hk);
printf("sizefo IntentCont is %d\n",sizeof(struct InterCont));
*/
/*
struct InterCont* pp=(struct InterCont*)malloc(sizeof(struct InterCont));
pp->key="ha";
pp->content="a person's info";
int res=save(pp);
printf("save ret=%d\n",res);
struct InterCont* mid=get(pp->key);
printf("try to print result get from cache..\n");
printInterCont(mid);
pp=(struct InterCont*)malloc(sizeof(struct InterCont));
pp->key="ah";
pp->content="another info";
res=save(pp);
printf("save ret=%d\n",res);
mid=get(pp->key);
printInterCont(mid);
}*/
/** ------------------server.c-------------------------**/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include "memserver.h"
/*
将字节流翻译为格式内容
*/
void decodeStream(struct InterCont* intercont,char* buf){
//struct InterCont* intercont=malloc(sizeof(struct InterCont));
char cmd[4];
char *key;
int bufIndex;
char *content;
int i,j,k;
//cmd
strncpy(cmd,buf,3);
cmd[3]='\0';
printf("get cmd:%s\n",cmd);
//key
int keylen;
keylen=(int)buf[3];
printf("get keylen=%d\n",keylen);
bufIndex=4;
if(keylen>0){
key=malloc(keylen+1);
for(i=0;i<keylen;i++){
key[i]=buf[bufIndex++];
}
key[i]='\0';
printf("key is %s\n",key);
}
intercont->key=key;
//content
bufIndex++;
bufIndex++;
printf("bufIndex=%d,strlen(buf)=%d\n",bufIndex,strlen(buf));
if(bufIndex<strlen(buf)){
content=malloc(strlen(buf)-bufIndex+1);
i=0;
while(bufIndex<strlen(buf)){
content[i++]=buf[bufIndex++];
}
content[i++]='\0';
printf("content is %s\n",content);
intercont->content=content;
}else{
intercont->content=NULL;
printf("no content!");
}
if(key!=NULL){
// free(key);
}
if(content!=NULL){
// free(content);
}
}
//thread function
void * doconnect(void *arg){
int client=*((int *)arg);
printf("do connect ,client=%d\n",client);
char buf[1024];
int len;
char cmd[4];
char* content;
char *key;
int keylen;
int contentlen;
struct InterCont* interCont;
int i,j;
while(1){
len=read(client,buf,1024);
buf[len]='\0';
fputs(buf,stdout);
char *ret;
if(strncmp(buf,"bye",3)==0){
printf("client try to close socket\n");
ret="i will close too";
write(client,ret,strlen(ret));
close(client);
pthread_exit((void*)0);
}else if(strncmp(buf,"bad",3)==0){
printf("client send bad cmd\n");
ret="bad cmd";
write(client,ret,strlen(ret));
}
else{
/* if(key!=NULL){
free(key);
}
if(content!=NULL){
free(content);
}
*/
ret="i have handle your request.";
//cmd,first 3 byte
strncpy(cmd,buf,3);
cmd[3]='\0';
printf("get cmd from client:%s,len=%d\n",cmd,strlen(cmd));
struct InterCont intercont;
//decode
decodeStream(&intercont,buf);
//get
if(strncmp(cmd,"get",3)==0){
//get key
interCont=get(intercont.key);
printInterCont(interCont);
if(interCont!=NULL){
write(client,interCont->content,strlen(interCont->content));
}else{
write(client,"null",4);
}
}
//
//sav
else if(strncmp(cmd,"sav",3)==0){
i=save(&intercont);
printf("save res=%d\n",i);
if(i==1){
write(client,"save ok",7);
}else{
write(client,"save fail!",10);
}
}
//
//del
else if(strncmp(cmd,"del",3)==0){
del(intercont.key);
write(client,"del ok",5);
}
//print mem info
else if(strncmp(cmd,"prt",3)==0){
printMemInfo();
write(client,"print ok",8);
}
else{
printf("unknow command!%s\n",cmd);
ret="unknown command.";
write(client,ret,strlen(ret));
}
}
}
}
int main(int argc,char* args[]){
if(argc!=2){
printf("usage:server 192.168.x.xx\n");
exit(-3);
}
struct sockaddr_in s_sock_addr,c_sock_addr;
int addr_size;
int s_fp,c_fp;
int port=0x1666;//固定端口
int bindres;
//construct
s_fp=socket(AF_INET,SOCK_STREAM,0);
if(s_fp==-1){
printf("fail to construct sock!\n");
exit(-1);
}
printf("after construct sock....\n");
//bind
bzero(&s_sock_addr,sizeof(struct sockaddr_in));
printf("ip addr:%s\n",args[1]);
s_sock_addr.sin_family=AF_INET;
s_sock_addr.sin_addr.s_addr=inet_addr(args[1]);
s_sock_addr.sin_port=htons(port);
bindres=bind(s_fp,(struct sockaddr*)(&s_sock_addr),sizeof(struct sockaddr));
if(bindres!=0){
printf("error bind!%s\n",strerror(bindres));
exit(1);
}
printf("after bind sock ... \n");
//listen
if(listen(s_fp,5)==-1){
printf("fail to listen!\n");
exit(2);
}
printf("after listen ....\n");
pthread_t tid;
int err;
while(1){
printf("in loop.....\n");
addr_size=sizeof(struct sockaddr_in);
c_fp=accept(s_fp,(struct sockaddr*)(&c_sock_addr),&addr_size);
if(c_fp==-1){
printf("accept fail!\n");
exit(3);
}
printf("get one client connect.cfp=%d ... \n",c_fp);
//new thread
err= pthread_create(&tid,NULL,doconnect,&c_fp);//交给工作线程
if(err!=0){
printf("fail to create thread!%s\n",strerror(err));
close(c_fp);
continue;
}
};
}
/**——————Makefile————————*/
main:server.o memserver.o
cc -o main server.o memserver.o -lpthread
server.o:server.c memserver.h
cc -c server.c
memserver.o:memserver.c
cc -c memserver.c
clean:
-rm *.o
//-----------------------------------------客户端-----------------------------------------------------//
/**-----client.c--------------*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
char data[1024];
//format use input to meet the procotol requirement
char* makestream(char* buf){
//char data[1024];
char cmd[4];
char* key;
int keylen;
strncpy(cmd,buf,3);
cmd[3]='\0';
strncpy(data,buf,3);//copy cmd
//key
int i=3,j=0,k=0;
int dataIndex=0;
if(buf[i]!=' '){
printf("usage:cmd key attachdata\n");
strncpy(data,"bad",3);
data[3]='\0';
return data;
}
i=4;
char tempkey[512];
j=0;
while(buf[i]!=' '&&buf[i]!='\n'){
tempkey[j++]=buf[i++];
}
tempkey[j]='\0';
keylen=strlen(tempkey);
printf("key is %s,kenlen=%d\n",tempkey,keylen);
data[3]=keylen;
dataIndex=4;
for(k=0;k<j;k++){
data[dataIndex++]=tempkey[k];
}
//if content is need
if(strncmp(cmd,"sav",3)==0){
printf("need content\n");
if(buf[i]!=' '){
printf("cmd %s need content,but not found!\n",cmd);
strncpy(data,"bad",3);
return data;
}
i++;
char tempcontent[1024];
j=0;
while(buf[i]!='\n'){
tempcontent[j++]=buf[i++];
}
if(j==0){
printf("cmd %s without content!\n",cmd);
strncpy(data,"bad",3);
return data;
}
//content len
data[dataIndex++]='0';
data[dataIndex++]='0';
for(k=0;k<j;k++){
data[dataIndex++]=tempcontent[k];
}
}else{
data[dataIndex++]=0;//just set the key len with 0
}
data[dataIndex++]='\0';//last char is terminal char
printf("data is %s\n",data);
return data;
}
struct InterCont* decodeStream(char* buf){
//struct InterCont* intercont=malloc(sizeof(struct InterCont));
char cmd[4];
char *key;
int bufIndex;
char *content;
int i,j,k;
//cmd
strncpy(cmd,buf,3);
cmd[3]='\0';
//intercont->key=cmd;
printf("get cmd:%s\n",cmd);
//key
int keylen;
keylen=(int)buf[3];
printf("get keylen=%d\n",keylen);
bufIndex=4;
if(keylen>0){
key=malloc(keylen+1);
for(i=0;i<keylen;i++){
key[i]=buf[bufIndex++];
}
key[i]='\0';
printf("key is %s\n",key);
}
//content
bufIndex++;
bufIndex++;
printf("bufIndex=%d,strlen(buf)=%d\n",bufIndex,strlen(buf));
if(bufIndex<strlen(buf)){
content=malloc(strlen(buf)-bufIndex+1);
i=0;
while(bufIndex<strlen(buf)){
content[i++]=buf[bufIndex++];
}
content[i++]='\0';
printf("content is %s\n",content);
}else{
printf("no content!");
}
if(key!=NULL){
// free(key);
}
if(content!=NULL){
// free(content);
}
return NULL;
}
void main(){
char buf[1024];
char one;
char *res;
int port=0x1666;
struct sockaddr_in s_sock_addr;
int c_sock;
c_sock=socket(AF_INET,SOCK_STREAM,0);
if(c_sock==-1){
printf("fail to construct client sock!\n");
exit(1);
}
bzero(&s_sock_addr,sizeof(struct sockaddr_in));
s_sock_addr.sin_family=AF_INET;
s_sock_addr.sin_addr.s_addr=inet_addr("192.168.1.105");
s_sock_addr.sin_port=htons(port);
//connect
if(connect(c_sock,(struct sockaddr*)(&s_sock_addr),sizeof(struct sockaddr))==-1){
printf("fail to connect to server!\n");
exit(5);
}
int len=0;
char ret[1024];
//loop,get cmd
while(1){
//printf("please input sth...\n");
res=fgets(buf,1024,stdin);
if(strncmp(buf,"quit",4)==0 || strncmp(buf,"bye",3)==0){
char* bye="bye";
write(c_sock,bye,3);
read(c_sock,ret,sizeof(ret));
break;
}
if(strlen(buf)==1){
continue;
}
printf("length=%d\n",strlen(buf));
fputs(res,stdout);
//write to server
char *data=makestream(buf);
if(strncmp(data,"bad",3)==0){
printf("bad command!\n");
continue;
}
// write to server
write(c_sock,data,strlen(data));
len=read(c_sock,ret,sizeof(ret));
ret[len]='\0';
printf("return from server:%s\n",ret);
};
close(c_sock);
}
/*** Makefile */
client:client.o
cc -o client client.o
client.o:client.c
cc -c client.c
clean:
-rm *.o client
下一步,学习一下当前的缓存服务器的源码,提高一下这方面的知识,再完善一下。特别是内存管理方面(申请,回收,如果提高效率)、内容持久化,同步方面,连接队列,等等。