1.客户端请求+服务器响应协议说明及流程
2.服务器接收http请求
3.服务器解析http请求
4.服务器响应http请求
5.并发处理
1.客户端请求+服务器响应


流程如下:

本质上就是读取字节流,解析字节流的信息,得到文件名,在服务器上找到文件,然后再按格式发出去。
注意:字段名: 后面是有空格的。请求头部结束是有回车换行的。
1.主函数
- #include
//sockaddr_in - #include
- #include
- #include
- #include
-
- #define SERVER_PORT 80
- typedef int socklen_t;
-
- static int debug = 0;
- int get_line(int sock,char *buf,int size);
- void* do_http_request(void* arg);
- void do_http_response(int client_sock,const char *path);
- void headers(int client_sock, FILE * resource);
- void cat(int client_sock, FILE* resource);
-
- //异常处理
- void bad_request(int client_sock);//400
- void unimplemented(int client_sock);//501
- void not_found(int client_sock);
-
-
-
-
- int httpmain(void)
- {
- //1.创建连接
- int sock;
- struct sockaddr_in server_addr;
- sock = socket(AF_INET,SOCK_STREAM,0);
- //bzero(&server_addr, sizeof(server_addr));//清空标签
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_addr.sin_port = htons(SERVER_PORT);
- bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
- listen(sock, 128);
-
- int done = 1;
-
- //2.解析http
- while (done)
- {
- struct sockaddr_in client;
- int client_sock, len, i;
- char client_ip[64];
- char buf[256];
- socklen_t client_addr_len;
-
- sock = socket(AF_INET, SOCK_STREAM, 0);
- //bzero(&server_addr, sizeof(server_addr));//清空标签
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_addr.sin_port = htons(SERVER_PORT);
-
- //打印客户端IP地址和端口号
- /*printf("client ip:%s\t port:%d\n",
- inet_ntop(AF_INET,&client.sin_addr,client_ip,sizeof(client_ip)),
- ntohs(client.sin_port));
- //读取客户端发送的数据
- len = read(client_sock, buf, sizeof(buf) - 1);
- buf[len] = '\0';
- //转换成大写
- for (i = 0; i < len; i++)
- {
- buf[i] = toupper(buf[i]);
- }
- len = write(client_sock, buf, len);
- printf("finished len:%d\n", len);
- close(client_sock);*/
-
- int* pclient_sock = NULL;
- //启动线程处理http请求
- pclient_sock = (int*)malloc(sizeof(int));
- *pclient_sock = client_sock;
- pthread_create(&id, NULL, do_http_request, pclient_sock);
- close(client_sock);
-
- }
- close(sock);
- return 0;
-
-
- }
3.服务器接收http请求
3.1读取每一行 int get_line(int sock, char* buf, int size)
- //读取每一行:返回值:-1:读取出错;0:读空行;>0:成功读取
- int get_line(int sock, char* buf, int size) {
- int count = 0;
- char ch = '\0';
- int len = 0;
- while ((count
-1)&&ch!='\n') - {
- len = read(sock, &ch, 1);
- if (len == 1)//如果读取正常
- {
- if (ch == '\r')//如果是回车
- {
- continue;
- }
- else if (ch == '\n')//如果是换行
- {
- break;
- }
- buf[count] = ch;
- count++;
- }
- else if(len==-1)//如果读取异常
- {
- count = -1;
- break;
- }
- }
- if (count >= 0)buf[count] = '\0';
- return count;
- }
-
-
3.2//解析请求:GET url 文件 void* do_http_request(void* arg);
如果碰到2个连续的回车换行即意味着请求头部结束了。
-
-
-
- void* do_http_request(void* arg) {
- int len = 0;
- char buf[256];
- char method[64];
- char url[256];
- char path[256];
- struct stat st;
- int client_sock = *(int*)arg; //为了被线程调用,必须要改成指针
-
- len = get_line(client_sock, buf, sizeof(buf)); //1.读取请求行
- if (len > 0)
- {
- int i = 0, j = 0;
- while (!isspace(buf[j]) && (i < sizeof(method) - 1))
- {
- method[i] = buf[j];
- i++;
- j++;
- }
- method[i] = '\0';
- if (debug) printf("request method:%s\n", method);
- if (strncasecmp(method,"GET",i)==0) //2.GET方法 strncasecmp:比较参数s1和s2字符串前n个字符
- {
- if (debug) printf("method=GET\n");
- while (isspace(buf[j++])); // isspace跳过白空格
- i = 0;
-
- while (!isspace(buf[j]) && (i < sizeof(url) - 1)) //3.得到URL
- {
- url[i] = buf[j];
- i++;
- j++;
- }
- url[i]= '\0';
- if (debug) printf("url:%s\n", url);
-
- char* pos = strchr(url, "?"); //4.获取文件 4.1截取 ?前的路径
- if (pos)
- {
- *pos = '\0';
- printf("real url: %s\n",url); //index.html ->./html_docs/index.html
- }
- sprintf(path, "./html_doc%s", url); //定位 只能截取一个/ path就是主目录下的全文件路径
- if (path[strlen(path)-1]=='/') //如果只有/那么就显示index.html
- {
- strcat(path, "index.html");
- }
- if (debug) printf("path:%s\n", path);
-
- do { //5.不断的读
- len = get_line(client_sock, buf, sizeof(buf));
- } while (len > 0);
-
- if (stat(path, $st) == -1) //6.判断文件是否存在
- {
- not_found(client_sock);
- }
- else
- {
- if (S_ISDOR(st.st_mode)) // 文件存在 S_ISDOR
- {
- strcat(path, "/index.html");
- }
-
- do_http_response(client_sock, path); //7.响应
- }
- }
- else {
- bad_request(client_sock); //8.出错处理
- }
- if (arg) free(arg);
- close(client_sock);
- return NULL;
- }
-
-
-
-
- }


4.服务器响应http请求
- void do_http_response(int client_sock,const char *path);
- void headers(int client_sock, FILE * resource);
- void cat(int client_sock, FILE* resource);
-
- //响应:把文件发出去
- void do_http_response(int client_sock, const char* path){
- FILE* resource = NULL;
- resource = fopen(path, "r");
- if (resource == NULL) {
- not_found(client_sock);
- }
- else
- {
- headers(client_sock, resource);
- cat(client_sock, resource);
- }
- fclose(resource);
- sleep(20);
- }
- void headers(int client_sock, FILE* resource){
- char buf[1024];
- char tmp[64];
- int fileid = 0;
- struct stat st;
-
- strcpy(buf,"HTTP/1.0 200 OK\r\n");
- strcpy(buf, "Server: Martin Server\r\n");
- strcpy(buf, "Content-Type text/html\r\n");
- strcpy(buf, "Connection:Close\r\n");
-
- fileid = fileno(resource);
- if (fstat(fileid, &st) == -1) {
- inner_error(client_sock);
- }
- snprintf(tmp,64,"Content-Length: %ld\r\n\r\n",st.st_size);
- strcat(buf, tmp);
- if (send(client_sock, buf, strlen(buf), 0) < 0)
- {
- fprintf(stderr,"send failed %s, %s\n",buf,strerror(errno));
- }
-
-
- }
-
- void cat(int client_sock, FILE* resource){
- char buf[1024];
- fgets(buf, sizeof(buf), resource);
- while (!feof(resource))
- {
- int len = write(client_sock, buf, strlen(buf));
- if (len > 0)printf("%s", buf);
- fgets(buf, sizeof(buf), resource);
- }
- }
发的内容就是字符串 header+body.

截取?之前的字符,得到文件名。
判断文件是否存在于服务器上 ,用struct stat st;

如果不存在 

内部错误
5.并发
就是用线程处理http的请求以下为注意点:
1.添加线程指针 int *pclient_sock=NULL;
2.函数返回值改成指针 void * do_http_request(void * pclient_sock)
3.函数传递的参数改成指针
void * do_http_request(void * pclient_sock)
{
int client_sock=*(int *)pclient_sock;
close(client_sock);
if(pclient_sock) free(pclient_sock);//释放动态分配的内存
return NULL;
}