首先放作者上传项目的地址https://sourceforge.net/projects/tinyhttpd/
本篇博客主要记录一下在学习这个项目时,通过查资料、看博客或看源码掌握的知识。

函数声明

//每次收到一个请求,创建一个线程来处理该请求
//把client_sock转成地址作为参数传入pthread_create
void accept_request(void *);

//错误请求,HTTP状态码400
void bad_request(int);

//读取文件
void cat(int ,FILE*);

//得到一行数据,读到\n就认为一行结束;读到\r就用MSG_PEEK的方式读入下一个字符
//若下个字符为'\n'则用recv读,否则将c设为'\n'
void get_line(int, char* , int);

//开启TCP连接,绑定端口
int startup(ushort *);

//如果不是GET或者POST,就报错方法没有实现
void unimplemented(int);

Http请求

GET / HTTP/1.1
Host: 192.168.0.103:5000
Connection: keep-alive 

startup()函数

setsockopt()

#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
/*******************************************************************
 * 目的:获取或设置与某个套接字关联的选项
 * sock:将要被设置或者获取选项的套接字
 * level:选项所在的协议层。为了操作套接字层的选项,将其指定为SOL_SOCKET
 * optname:需要访问的选项名
 * optval:对于setsockopt(),指向包含新选项值的缓冲
 * optlen:现选项的长度
 返回值:
 * 成功执行返回0,失败返回-1
********************************************************************/
//项目httpd.c中实际写法,参照理解
if((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    error_die("setsockopt failed");

bind()给套接字命名

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr* addr, socklen_t* addrlen);
/***********************************************************************
 * 当用socket()函数创建套接字后,套接字在网络地址族中存在,但没有任何地址给它
 * 赋值。bind()函数把用addr指定的地址赋给用文件描述符代表的套接字sockfd。
 * 这个操作一般被称作bind()函数用本地地址给套接字命名。
 * 返回值:0,成功;-1,失败。
************************************************************************/
//项目实际写法
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
        error_die("bind");

动态分配一个端口

if(*port==0)
{
    socklen_t    namelen=sizeof(name);
    if(getsockname(httpd, (struct sockaddr*) &name, &namelen) == -1)
        error_die("getsockname");
    *port = ntohs(name.sin_port);
}

main()

accept函数

接受套接字中已经建立的连接

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen);
//accept函数提取所监听套接字的等待队列中第一个连接请求,创建一个新的套接字。新建立的套接字不在监听状态
//原来监听的套接字也不受accept的影响。

//项目实际写法
client_sock = accept(sever_sock, (struct sockaddr *)&client_name, &client_name_len);
if(client_sock==-1)
    error_die("accept");

pthread_create函数

创建线程,也就是确定调用该线程函数的入口点

#include <pthread.h>
pthread_t newthread;  //pthread_t在linux中被定义为"unsigned long int"
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine) (void*), void *arg)
//参数说明
//thread:线程标识符
//attr:线程属性设置

//项目实际写法
if(pthread_create(&newthread, NULL, (void *)accept_request, (void*) (intptr_t)client_sock)!=0)
    perror("pthread_create");

accept_request函数

处理从套接字上监听到的一个http请求

get_line函数

用处:从缓冲区中读取字符存入字符数组buf中

//中止条件为换行符
//例如读取 GET / HTTP/1.1
int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c='\0';
    int n;
    while((i<size-1) && c!='\n')
    {
        n = recv(sock, &c, 1, 0);
        if(n>0)
        {
            if(c=='\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);
                if(n>0 && c=='\n')
                    recv(sock, &c, 1, 0);
                else
                    c=='\n';
            }
            buf[i]==c;
            i++;
        }
        else
            c='\n';
    }
    buf[i]='\0';
    return i;
}

unemplemented函数

用处:通知客户端请求的web方法没有实现
参数:客户端套接字(client socket)

void unemplemented(int client)
{
    char buf[1024];
    sprintf(buf, "HTTP/1.0 501 Method Not Implemented");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING); //#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
    send(client, buf, strlen(buf), 0);
}