一、在TcpConnection 中多添加和http协议相关的request和response
struct TcpConnection {
struct EventLoop* evLoop;
struct Channel* channel;
struct Buffer* readBuf;
struct Buffer* writeBuf;
char name[32];
// http协议
struct HttpRequest* request;
struct HttpResponse* response;
};
二、给客户端回复数据(方法一)
1.在Buffer.h文件中添加bufferSendData函数:
// 发送数据
int bufferSendData(struct Buffer* buf,int socket);
// 发送数据
int bufferSendData(struct Buffer* buf,int socket) {
// 判断有无数据
int readableSize = bufferReadableSize(buf);// 这些未读的数据就是待发送的数据
if(readableSize > 0) {
int count = send(socket,buf->data + buf->readPos,readableSize,MSG_NOSIGNAL);
if(count > 0) {
buf->readPos += count;
usleep(1);
}
return count;
}
return 0;
}
2.在TcpConnection.c文件中添加processWrite函数:
int processWrite(void* arg) {
struct TcpConnection* conn = (struct TcpConnection*)arg;
// 发送数据
int count = bufferSendData(conn->writeBuf,conn->channel->fd);
if(count > 0) {
// 判断数据是否被全部发送出去了
if(bufferReadableSize(conn->writeBuf) == 0){
// 1.不再检测写事件 -- 修改channel中保存的事件
writeEventEnable(conn->channel,false);
// 2.修改dispatcher检测的集合 -- 添加任务节点
eventLoopAddTask(conn->evLoop,conn->channel,MODIFY);
// 3.删除这个节点
eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
}
}
return 0;
}
3.修改tcpConnectionInit函数中调用的channelInit函数的写回调函数为processWrite函数
// 初始化
struct TcpConnection* tcpConnectionInit(int fd,struct EventLoop* evLoop) {
struct TcpConnection* conn = (struct TcpConnection*)malloc(sizeof(struct TcpConnection));
conn->evLoop = evLoop;
struct Channel* channel = channelInit(fd,ReadEvent,processRead,processWrite,tcpConnectionDestroy,conn);
conn->channel = channel;
conn->readBuf = bufferInit(10240); // 10k
conn->writeBuf = bufferInit(10240); // 10k
sprintf(conn->name,"TcpConnection-%d",fd);
// http协议
conn->request = httpRequestInit();
conn->response = httpResponseInit();
// 把channel添加到事件循环对应的任务队列里边
eventLoopAddTask(evLoop,conn->channel,ADD);
return conn;
}
三、给客户端回复数据(方法二)
- 在TcpConnection.h中添加
// #define MSG_SEND_AUTO
- TcpConnection.c
// 接收客户端数据
int processRead(void* arg) {
struct TcpConnection* conn = (struct TcpConnection*)arg;
// 接收数据
int count = bufferSocketRead(conn->readBuf,conn->channel->fd);
if(count > 0) {
// 接收到了Http请求,解析Http请求
int socket = conn->channel->fd;
#ifdef MSG_SEND_AUTO // 给客户端回复数据的方式一
writeEventEnable(conn->channel,true);
eventLoopAddTask(conn->evLoop,conn->channel,MODIFY);
#endif
bool flag = parseHttpRequest(conn->request,conn->readBuf,conn->response,conn->writeBuf,socket);
if(!flag) {
// 解析失败,回复一个简单的html
char* errMsg = "Http/1.1 400 Bad Request\r\n\r\n";
bufferAppendString(conn->writeBuf,errMsg);
}
}
else{
#ifdef MSG_SEND_AUTO
// 断开连接
eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
#endif
}
#ifndef MSG_SEND_AUTO
// 断开连接
eventLoopAddTask(conn->evLoop,conn->channel,DELETE);
#endif
return 0;
}
1.修改HttpRequest.c文件中的sendFile函数和sendDir函数
void sendFile(const char* fileName,struct Buffer* sendBuf,int cfd) {
// 打开文件
int fd = open(fileName,O_RDONLY);
if(fd < 0) {
perror("open");
return;
}
// assert(fd > 0);
#if 1
while (1) {
char buf[1024];
int len = read(fd,buf,sizeof(buf));
if(len > 0) {
// send(cfd,buf,len,0);
bufferAppendData(sendBuf,buf,len);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
bufferSendData(sendBuf,cfd);
#endif
}
else if(len == 0) {
break;
}
else{
close(fd);
perror("read");
}
}
#else
// 把文件内容发送给客户端
off_t offset = 0;
int size = lseek(fd,0,SEEK_END);// 文件指针移动到了尾部
lseek(fd,0,SEEK_SET);// 移动到文件头部
while (offset < size){
int ret = sendfile(cfd,fd,&offset,size - offset);
printf("ret value: %d\n",ret);
if (ret == -1 && errno == EAGAIN) {
printf("没数据...\n");
}
}
#endif
close(fd);
}
void sendDir(const char* dirName,struct Buffer* sendBuf,int cfd) {
char buf[4096] = {0};
sprintf(buf,"<html><head><title>%s</title></head><body><table>",dirName);
struct dirent** nameList;
int num = scandir(dirName,&nameList,NULL,alphasort);
for(int i=0;i<num;i++) {
// 取出文件名 nameList 指向的是一个指针数组 struct dirent* tmp[]
char* name = nameList[i]->d_name;
struct stat st;
char subPath[1024] = {0};
sprintf(subPath,"%s/%s",dirName,name);
stat(subPath,&st);
if(S_ISDIR(st.st_mode)) {
// 从当前目录跳到子目录里边,/
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",
name,name,st.st_size);
}else{
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",
name,name,st.st_size);
}
// send(cfd,buf,strlen(buf),0);
bufferAppendString(sendBuf,buf);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
bufferSendData(sendBuf,cfd);
#endif
memset(buf,0,sizeof(buf));
free(nameList[i]);
}
sprintf(buf,"</table></body></html>");
// send(cfd,buf,strlen(buf),0);
bufferAppendString(sendBuf,buf);
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
bufferSendData(sendBuf,cfd);
#endif
free(nameList);
}
2.修改HttpResponse.c文件的httpResponsePrepareMsg函数
// 组织http响应数据
void httpResponsePrepareMsg(struct HttpResponse* response,struct Buffer* sendBuf,int socket) {
// 状态行
char tmp[1024] = {0};
sprintf(tmp,"HTTP/1.1 %d %s\r\n",response->statusCode,response->statusMsg);
bufferAppendString(sendBuf,tmp);
// 响应头
for(int i=0;i<response->headerNum;++i) {
// memset(tmp,0,sizeof(tmp)); ?????????
sprintf(tmp,"%s: %s\r\n",response->headers[i].key,response->headers[i].value);
bufferAppendString(sendBuf,tmp);
}
// 空行
bufferAppendString(sendBuf,"\r\n");
#ifndef MSG_SEND_AUTO // 给客户端回复数据(方法二)
bufferSendData(sendBuf,socket);
#endif
// 回复的数据
response->sendDataFunc(response->fileName,sendBuf,socket);
}
四、释放资源 tcpConnectionDestroy
// 释放资源
int tcpConnectionDestroy(void* arg);
// 释放资源
int tcpConnectionDestroy(void* arg) {
struct TcpConnection* conn = (struct TcpConnection*)arg;
if(conn!=NULL) {
if (conn->readBuf && bufferReadableSize(conn->readBuf) == 0 &&
conn->writeBuf && bufferReadableSize(conn->writeBuf) == 0) {
destroyChannel(conn->evLoop,conn->channel);
bufferDestroy(conn->readBuf);
bufferDestroy(conn->writeBuf);
httpRequestDestroy(conn->request);
httpResponseDestroy(conn->response);
free(conn);
}
}
return 0;
}
五、日志功能
#pragma once
#include <stdarg.h>
#include <stdio.h>
#define DEBUG 1
#if DEBUG
/*
* 如果不加 do ... while(0) 在进行条件判断的时候(只有一句话), 省略了{}, 就会出现语法错误
* if
* xxxxx
* else
* xxxxx
* 宏被替换之后, 在 else 前面会出现一个 ; --> 语法错误
*/
#define LOG(type, fmt, args...) \
do{\
printf("%s: %s@%s, line: %d\n***LogInfo[", type, __FILE__, __FUNCTION__, __LINE__);\
printf(fmt, ##args);\
printf("]\n\n");\
}while(0)
#define Debug(fmt, args...) LOG("DEBUG", fmt, ##args)
#define Error(fmt, args...) do{LOG("ERROR", fmt, ##args);exit(0);}while(0)
#else
#define LOG(fmt, args...)
#define Debug(fmt, args...)
#define Error(fmt, args...)
#endif