歡迎您光臨本站 註冊首頁

Linux 多線程伺服器源代碼詳解

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

對http://tuoxie174.blog.51cto.com/1446064/413189 的源代碼分析

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
using namespace std;

#define PORT 8848 //Port
#define BACKLOG 5 // Max Link num
#define MAXDATASIZE 1000 // Max data buf
#define DEBUG 1

//1. Func Decalare
void process_cli(int connectfd, sockaddr_in client);
int sendobj(int connectfd, char* serverfilepath);
int isDIR(char* path);
int fileordirExist(char* fpath);
char* gettextname(char*);
int writehead(FILE* cfp, char* extname);
void* start_routing(void* arg);
void msg404(int connectfd);

//2. Structure Decalare
struct ARG
{
int connfd;
sockaddr_in client;
};


int main()
{
//cout << "Hello world!" << endl;
int listenfd, connectfd;
//! 1. thread id
pthread_t thread;
//! 2. pass this var to the thread
ARG* arg;
struct sockaddr_in server;
struct sockaddr_in client;
int sin_size;

//! 3. create tcp socket
#ifdef DEBUG
printf("socket...n");
#endif
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("creating socket failed!n");
exit(1);
}
//! 4. set the socket -- close, timeout
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));


bzero(&server, sizeof(server));

server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);

printf("bind...n");
//! 5. bind a ip and port to the socket
if(bind(listenfd, (struct sockaddr*)&server, sizeof(struct sockaddr)))
{
perror("bind error!");
exit(1);
}

printf("listen...n");
//! 6. listen the socket
if(listen(listenfd,BACKLOG)==-1)
{
perror("listen error!n");
exit(1);
}

// a datastructure for programmer to use struct sockaddr
sin_size = sizeof(struct sockaddr_in);
while(1)
{
// begin to process the client's connect
//! 7. accept using main thread
printf("accepting...n");

if((connectfd=accept(listenfd, (struct sockaddr*)&client, (socklen_t*)&sin_size))==-1)
{
printf("accept error!n");
}

//connectfd is a socket
arg = new ARG;
arg->connfd = connectfd;
memcpy((void*)&arg->client, &client, sizeof(client));

#ifdef DEBUG
printf("thread creating...n");
#endif
//! 8. invoke start_routing to handle this thread. -- start_routing is a callback func
if(pthread_create(&thread, NULL, start_routing, (void*)arg))
{
perror("pthread_create error!n");
exit(1);
}

}
close(listenfd);

return 0;
}


//! 1.1 handle the request
void process_cli(int connectfd, sockaddr_in client)
{
int num;
char requestline[MAXDATASIZE];
char filepath[MAXDATASIZE];
char cmd[MAXDATASIZE];
char extname[MAXDATASIZE];

int c;
FILE *fp;
//! Because everything is file in UNIX
//! 1.1.1 open the client socket
fp = fdopen(connectfd, "r");

#ifdef DEBUG
printf("the host is: %s n", inet_ntoa(client.sin_addr));
#endif
fgets(requestline, MAXDATASIZE, fp);

#ifdef DEBUG
printf("The request is: %s n", requestline);
#endif

strcpy(filepath, "./");
sscanf(requestline, "%s%sn", cmd, filepath 2);
//! use func gettextname
strcpy(extname, gettextname(filepath));

#ifdef DEBUG
printf("cmd: %sn filepath: %sn extname: %sn", cmd, filepath, extname);
printf("string comparingn:::::::start:::::::n");
#endif

if(strcmp(cmd, "GET")==0)
{
#ifdef DEBUG
printf("cmd(%s)==GETn", cmd);
#endif

if(fileordirExist(filepath))
{
if(isDIR(filepath))
{
//! is a DIR
#ifdef DEBUG
printf("%s is a DIR n", filepath);
#endif
if(fileordirExist(strcat(filepath, "index.html")))
{
//! send
sendobj(connectfd, "index.html");
}
else
{
msg404(connectfd);
}


}
else
{
//! is a file
#ifdef DEBUG
printf("%s is a file n", filepath);
#endif
//! send
sendobj(connectfd, filepath);
}
}
else
{
#ifdef DEBUG
printf("404n");
#endif
msg404(connectfd);
}
}
else
{
#ifdef DEBUG
printf("cmd(%s)!=GETn", cmd);
#endif
}
#ifdef DEBUG
printf("::::::::::::::::::::end::::::::::::::::::n");
close(connectfd);
#endif
}

//! 1.2
void msg404(int connectfd)
{
char* msg;
msg = "HTTP/1.0 404 Not Found Content-Type: text/plain 404 not found by Manion";
send(connectfd, msg, strlen(msg), 0);
}

//! 1.3 is the filepath a file or direatory
int fileordirExist(char* fpath)
{
struct stat filestat;
return ( stat(fpath, &filestat)!=-1);
}

//! 1.4 is the filepath a directory
int isDIR(char* fpath)
{
#ifdef DEBUG
printf("IN IsDirns");
#endif
struct stat filestat;
return ( stat(fpath, &filestat)!=-1 && S_ISDIR(filestat.st_mode));
}

//! 1.5 send the data of the file which the client want
int sendobj(int connectfd, char* serverfilepath)
{
//printf("server: %sn", serverfilepath)
FILE* sfp=NULL, *cfp=NULL;
char c;
sfp = fopen(serverfilepath, "r");
cfp = fdopen(connectfd, "w");

if(sfp == NULL)


printf("can't open the file!... n");
if(cfp == NULL)
printf("can't open the link file...!n");

writehead(cfp, gettextname(serverfilepath));

while( (c=getc(sfp)) !=EOF){
putc(c, cfp);
//putchar(c);
}
fflush(cfp);
return 0;
}

//! 1.6 write the packet header to the client
int writehead(FILE* cfp, char* extname)
{
#ifdef DEBUG
printf("INWRITEHEAD:::::extname is %s::::::n", extname);
#endif
char* content = "text/plain";
if(strcmp(extname, "html")==0 || strcmp(extname, "htm")==0)
content = "text/html";
else if(strcmp(extname,"css")==0)
content = "text/css";
else if(strcmp(extname,"gif")==0)
content = "image/gif";
else if(strcmp(extname,"jpeg")==0||strcmp(extname,"jpg")==0)
content = "image/jpeg";
else if(strcmp(extname, "png")==0)
content = "image/png";

#ifdef DEBUG
printf("HTTP/1.1 200 OKn");
printf("Content-Type: %s n", content);
#endif

fprintf(cfp, "HTTP/1.1 200 OK ");
fprintf(cfp, "Content-Type: %s n", content);

return 0;
}

//! 1.7 get the extent name of the file
char* gettextname(char* filepath)
{
char* p;
if((p=strrchr(filepath,'.'))!=NULL)
return p 1;
return NULL;
}

//! 1.8 invoked by pthread_create
void* start_routing(void* arg)
{
ARG* info;
info = (ARG*) arg;


// handle client's requirement
process_cli(info->connfd, info->client);
delete arg;
pthread_exit(NULL);
}

//1. 只要 g -g -o execname filename -lpthread

//2. 然後在瀏覽器輸入: 127.0.0.1:8848

//3. 與程序同目錄下 有 index.html (注意擴展名, 程序中為判斷)

問題: 中文會出現亂碼, 查看源代碼, 發現<head> 的很大一部分沒有了, 只留下後面的, 於是乎編碼沒有設置. (英文網頁時,前半部分也沒了). 繼續解決.






















本文出自 「咖啡時間」 博客,請務必保留此出處http://tuoxie174.blog.51cto.com/1446064/413683


[火星人 ] Linux 多線程伺服器源代碼詳解已經有706次圍觀

http://coctec.com/docs/linux/show-post-47901.html