一、主线功能
1、设计与实现在线商城系统涉及到前端展示、后台管理以及数据库进行查找,功能包含登录页面、商品搜素、商品详细信息查找。
二、页面设计
2.1、商品搜索设计
2.2、商品详细信息展示设计
2.3、TCP并发服务器设计
HTTP是基于Tcp服务器搭建起来的
第一步,建立连接
//创建套接字并且监听
int create_sever(const char *ip ,unsigned short port)
{
int option,optlen;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("fail socket");
return -1;
}
optlen=sizeof(option);
option=1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&option,optlen);
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(port);
ser.sin_addr.s_addr = inet_addr(ip);
int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
if (-1 == ret)
{
perror("fail bind");
return -1;
}
ret = listen(sockfd, 128);
if (-1 == ret)
{
perror("fail listen");
return -1;
}
return sockfd;
}
第二步、接收监听队列里面的套接字,并产生通信的套接字
int connfd = accept(sockfd, NULL, NULL);
if (-1 == connfd)
{
perror("fail accept");
continnue;
}
至此三次握手建立完毕
第三步、接收网页传过来的HTTP报文
//接收报文
int recv_http_request(int connfd,char *http_req,int maxlen)
{
memset(http_req,0,maxlen);
ssize_t size = recv(connfd,http_req,maxlen,0);
if(size <= 0)
{
perror("fail1 recv");
return size;
}
return size;
}
//将接收的内容进行解析,解析分为三步,方法、url、内容
int parse_http_request(char *http_req,HTTP_REQ_t *phttp)
{
char buf[256] = {0};
memset(phttp,0,sizeof(HTTP_REQ_t));
char *p = strtok(http_req," ");
if(NULL == p)
{
return -1;
}
strcpy(phttp->method,p);
p = strtok(NULL," ");
if(NULL == p)
{
return -1;
}
strcpy(phttp->url,p);
p = strtok(NULL,"\0");
if(NULL == p)
{
return -1;
}
p = strstr(p,"\r\n\r\n");
if(NULL == p)
{
return -1;
}
strcpy(phttp->content,p+4);
parse_phttp(phttp->content,buf);
strcpy(phttp->content,buf);
printf("--->%s\n",phttp->content);
return 0;
}
第四步、对接收的报文需要的内容,进行发送
//先发送报文的头部
int send_http_head(int connfd)
{
char *head ="HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Sever: my-web-sever\r\n"
"Connection: keep-alive\r\n\r\n";
ssize_t size = send(connfd,head,strlen(head),0);
if(size < 0)
{
perror("fail send");
return -1;
}
return 0;
}
//打开需要的文件的资源,并将该资源进行发送
int send_http_file(int connfd,char *filename)
{
char buf[1024] = {0};
int fd = open(filename,O_RDONLY);
if(fd < 0)
{
perror("fail open file");
return -1;
}
while(1)
{
ssize_t size = read(fd,buf,sizeof(buf));
if(size <= 0)
{
break;
}
send(connfd,buf,size,0);
}
close(fd);
return 0;
}
//发送响应请求
int send_http_reponse(int connfd,HTTP_REQ_t *phttp)
{
char *p;
int i = 0;
char sfile[1024] = {0};
send_http_head(connfd);
char *q;
p = strtok(phttp->url,"?");
q = strtok(NULL,"\0");
printf("%s",phttp->url);
if(!strcmp(phttp->method,"GET"))//请求方法
{
if(!strcmp(phttp->url,"/"))
{
sprintf(sfile,"./res/denglu.html");
send_http_file(connfd,sfile);
return 0;
}
else if(strstr(phttp->url,".jpg") || strstr(phttp->url,".png"))
{
if(strstr(phttp->url,"200905"))
{
sprintf(sfile,".%s",phttp->url);
send_http_file(connfd,sfile);
}
else
{
sprintf(sfile,"./res%s",phttp->url);
send_http_file(connfd,sfile);
}
return 0;
}
else if(strstr(phttp->url,"/favicon.ico"))//图标
{
return 0;
}
else if(strstr(phttp->url,".html"))
{
if(strcmp(phttp->url,"/4.html")==0)//特殊的,四级页面,我们根据请求直接将网页的标签内容在此写好进行发送
{
sprintf(sfile,"./res%s",phttp->url);
send_http_file(connfd,sfile);
record = connfd;
char *site = index(q,'=');
select_sqlite4(site+1);
char buf[100] = "<body><html>";
send(connfd,buf,strlen(buf),0);
}
else
{
sprintf(sfile,"./res%s",phttp->url);
send_http_file(connfd,sfile);
}
}
}
else if(!strcmp(phttp->method,"POST"))
{
if(strstr(phttp->url,".html"))
{
if(strcmp(phttp->url,"/meaning.html") == 0)
{
sprintf(sfile,"./res%s",phttp->url);//与上述想法同理
send_http_file(connfd,sfile);
printf("sendfile = %s\n",sfile);
record = connfd;
select_sqlite(phttp->content);
char buf[100] = "<body><html>";
send(connfd,buf,strlen(buf),0);
return 0;
}
sprintf(sfile,"./res%s",phttp->url);
send_http_file(connfd,sfile);
printf("sendfile = %s\n",sfile);
return 0;
}
}
}
三、函数端口
接口 | 参数 | 返回值 | 接口描述 |
Intcallback(void*arg,intcolumn_cnt,char **column_value,char **column_name) | 查到数据列数,地址集合,每一列列名的集合 | 0 | 回调函数 |
int select_sqlite(char *p) | 数据库指针 | fd | 数据库查找 |
int create_sever(constchar*ip ,unsigned short port) | 地址,端口 | 0 | 创造数据服务器端口号 |
int epoll_del_fd(int epfds,int fd) | 文件描述符集合句柄,端口 | 0 | 删除文件描述符,从数组 |
int epoll_add_fd(int epfds,int fd,uint32_t event) | 文件描述符集合句柄,端口 | 0 | 未来世界 |
intrecv_http_request(intconnfd,char *http_req,int maxlen) | 文件描述符,结构体指针,最大长度 | 0 | 接受TCP报文请求 |
intparse_http_request(char*http_req,HTTP_REQ_t *phttp) | 结构体对象指针,请求对象 | 0 | 对请求报文进行解析 |
int send_http_head(int connfd) | 文件描述符 | 0 | 发送报文的头部 |
int send_http_file(int connfd,char *filename) | 文件描述符,文件 | 0 | 发送报文想要的内容 |
intsend_http_reponse(intconnfd,HTTP_REQ_t *phttp) | 文件描述符,结构体对象 | 0 | 发生报文进行服务器响应 |
四、并发服务器的设计
4.1、并发设计
主要用的是IO多路复用的epoll 进行设计,其资源消耗相对来说比较小,对于小型网页来说,完全够用,且效率高
4.2、设计过程
第一步、将创建好的套接字的文件描述添加到数组里面
第二步、遍历数组、查找当前的套接字
第三步、执行当前套接字所需运行的内容
//主函数 ,并发服务器
int main(int agrc,char *agrv[])
{
int connfd = 0;
char buf[1024] = {0};
HTTP_REQ_t http;
char http_req[4096] = {0};
int ret = 0;
int sockfd = create_sever("192.168.208.85",8080);
if(sockfd == -1)
{
perror("fail socket");
return -1;
}
int epfds = epoll_create(1024);
if(-1 == epfds)
{
perror("fail epoll_create");
return -1;
}
epoll_add_fd(epfds,sockfd,EPOLLIN);
struct epoll_event evs[1024];
while(1)
{
int cnt = epoll_wait(epfds,evs,1024,-1);
if(cnt < 0)
{
perror("fail epoll_wait");
return -1;
}
for(int i=0;i<cnt;i++)
{
if(sockfd == evs[i].data.fd)
{
int connfd = accept(sockfd, NULL, NULL);
if (-1 == connfd)
{
perror("fail accept");
continue;
}
epoll_add_fd(epfds, connfd, EPOLLIN);
}
else
{
memset(http_req,0,sizeof(http_req));
recv_http_request(evs[i].data.fd,http_req,sizeof(http_req));
printf("----------\n%s\n----------\n",http_req);
ret = parse_http_request(http_req,&http);
if(ret < 0)
{
close(evs[i].data.fd);
continue;
}
printf("method : %s\n",http.method);
printf("url : %s\n",http.url);
printf("content: %s\n",http.content);
printf("----------------------------\n");
int size = send_http_reponse(evs[i].data.fd,&http);
if(size < 0)
{
perror("fail2 recv");
epoll_del_fd(epfds,evs[i].data.fd);
close(evs[i].data.fd);
continue;
}
close(evs[i].data.fd);
}
}
}
return 0;
}
//增加此刻的文件描述符到数组中
int epoll_add_fd(int epfds,int fd,uint32_t event)
{
struct epoll_event ev;
ev.events = event;
ev.data.fd = fd;
int ret = epoll_ctl(epfds,EPOLL_CTL_ADD,fd,&ev);
if(-1 == ret)
{
perror("fail epoll_ctl add");
return -1;
}
return 0;
}
//删除数组中的文件描述符
int epoll_del_fd(int epfds,int fd)
{
int ret = epoll_ctl(epfds, EPOLL_CTL_DEL, fd, NULL);
if (ret < 0)
{
perror("fail epoll_ctl del");
return -1;
}
return 0;
}
五、网页效果