设置端口号
我这里设置的是1234
#define SERVER_PORT 1234
创建套接字
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, 0);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
//每个字节都用0填充
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听本地所有IP地址
serv_addr.sin_port = htons(SERVER_PORT); //端口
//绑定
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
printf("wait client connect...\n");
return 0;
}
接收客户端请求
int main(){
//...
//printf("wait client connect...\n");
int done = 1;
while (done){
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
char client_ip[64];
char buf[256];
//打印客户端ip地址和端口号
printf("client ip: %s\t port: %d\n",
inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),
ntohs(clnt_addr.sin_port));
//关闭套接字
close(clnt_sock);
}
//关闭套接字
close(serv_sock);
return 0;
}
获取服务器的ip地址
ifconfig
编译代码并运行
gcc server.c -o server
./server
在浏览器上输入
http://ip:port
程序输出如下:
获取客户端http请求数据
客户端请求消息
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
定义一个get_http_line(int clnt_sock, char * buf, int size)的函数,读取socket缓冲区的一行数据,放回-1读取失败,否则返回这一行数据的字符个数。
int get_http_line(int clnt_sock, char * buf, int size){
int cnt = 0; //这一行数据的字符个数
char ch = '\0';
int len = 0;
while (cnt < size - 1){
//读取socket缓冲区的一个字符
len = read(clnt_sock, &ch, 1);
if (len == 1){
//读取成功
if (ch == '\r'){
//吸收回车符
continue;
}else if (ch == '\n'){
break;
}
buf[cnt++] = ch;
}else if (len == -1){
//读取失败
fprintf(stderr, "read failed\n");
cnt = -1;
break;
}else {
//客户端主动关闭
fprintf(stderr, "client close\n");
cnt = -1;
break;
}
}
if (cnt >= 0){
buf[cnt] = '\0';
}
return cnt;
}
定义一个do_http_request(int clnt_sock)的函数,实现获取客户端http请求的数据
void do_http_request(int clnt_sock){
int len = 0;
char buf[256];
//打印数据
do{
len = get_http_line(clnt_sock, buf, sizeof(buf));
if (debug) printf("%s\n", buf);
}while (len > 0);
}
在main函数这个位置调用do_http_request函数
//获取客户端http请求数据
do_http_request(clnt_sock);
编译并运行程序,输出如下:
实现Http解析
根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
这里我只完成了GET的请求方法,其他请求 暂不处理。
重写do_http_request(int clnt_sock)函数
#include <sys/stat.h>
#include <errno.h>
struct stat st;
static int debug = 1;
static char parent_path[] = "."; //存放html文件的主目录,"."为相对路径
void do_http_request(int clnt_sock){
int len = 0;
char buf[256];
char method[16]; //请求方法
char url[256]; //请求方法
char path[260]; //请求的文件、路径
//获取请求行(request line)
len = get_http_line(clnt_sock, buf, sizeof(buf));
if (len > 0){
int i = 0, j = 0;
//获取请求方法
while (buf[j] != ' ' && i < sizeof(method) - 1){
method[i++] = buf[j++];
}
method[i] = '\0';
if (debug) printf("request method: %s\n", method);
if (strncasecmp(method, "GET", i) == 0){
//get请求
while (buf[j++] != ' ');
i = 0;
//获取请求url
while (buf[j] != ' ' && i < sizeof(url) - 1){
url[i++] = buf[j++];
}
url[i] = '\0';
if (debug) printf("request url: %s\n", url);
//TODO:获取请求参数
//去掉请求参数
char *last = strchr(url, '?');
if (last) *last = '\0';
if (debug) printf("real request url: %s\n", url);
//获取请求文件、路径
sprintf(path, "%s%s", parent_path, url);
if (debug) printf("request path: %s\n", path);
//判断文件是否存在
if (stat(path, &st) == -1){
fprintf(stderr, "stat %s find failed. reason: %s\n", path