day33:零基础学嵌入式之网络——HTTP服务端
一、服务器
1.服务器分类
- 单循环服务器:只能处理一个客户端任务的服务器
- 并发服务器:可同时处理多个客户端任务的服务器
二、TCP并发服务器的构建
1.如何构建?
(1)多进程(每一次创建都非常耗时耗空间,但是安全)
#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
void do_wait(int signo)
{wait(NULL);
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}signal(SIGCHLD, do_wait);char buf[1024] = {0};struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}pid_t pid = fork();if (pid > 0){}else if (0 == pid){while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d] : %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(connfd);exit(1);}else{perror("fork fail");return 1;}}close(sockfd);return 0;
}
(2)多线程()
#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
typedef struct
{int connfd;struct sockaddr_in cliaddr;
} XIN;void do_thurance(void *arg)
{XIN xi = *(XIN *)arg;char buf[1024] = {0};while (1){memset(buf, 0, sizeof(buf));ssize_t size = recv(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");break;}else if (0 == size){printf("client connet offline");break;}printf("[%s] [%d] : %s\n", inet_ntoa(xi.cliaddr.sin_addr), ntohs(xi.cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(xi.connfd, buf, sizeof(buf), 0);if (size < 0){perror("fail send");break;}}close(xi.connfd);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50000);if (sockfd < 0){return 1;}char buf[1024] = {0};pthread_t tid;struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);while (1){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);if (connfd < 0){perror("connect fail");return 1;}printf("client getline\n");XIN xi;xi.connfd = connfd;xi.cliaddr = cliaddr;pthread_create(&tid, NULL, do_thurance, &xi);pthread_detach(tid); //设置分离属性,线程结束,操作系统自动会回收;}close(sockfd);return 0;
}
(3)线程池
- 主要解决:程序运行过程中,线程被反复创建和销毁带来的耗时问题;
(4)IO多路复用
理解:不创建进程和线程的情况下,对多个文件描述符监测复用一个进程;
二、IO多路复用
1.阻塞IO方式:
(1)多个IO之间是同步关系;
(2)多个IO之间相互影响;
2.IO多路复用
(1)步骤
1)创建文件描述符集合(数组、链表、树形结构.......);
2)添加关注的文件描述符带集合中;
3)通过函数接口,把集合传递给内核,并开始检测IO事件(输入输出、读写事件);
4)当内核检测到事件时,通过相关函数返回,做具体的相关操作;
(2)select
1)创建文件描述符集合表:fd_set
2)清楚集合表
void FD_CLR(int fd, fd_set *set);//把fd清掉
int FD_ISSET(int fd, fd_set *set);//查看fd在这个表中有没有
void FD_SET(int fd, fd_set *set);//把fd放进集合表中
void FD_ZERO(fd_set *set);//把集合表整体清空
3)把文件描述符加入到集合表中
4)select:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:通知内核检测的集合表并开始检测
参数:
nfds:关注的最大描述符+1
readfds:关注的读事件的我文件描述符的地址
writefds:关注的写事件的我文件描述符的地址
exceptfds:其他事件
timeout:超时事件的地址;设置一个时间结点,如果都没有事件来,就直接返回;NULL:不设置超时时间
返回值:
成功:返回到达事件的个数
失败:-1
超时时间到达没有事件时:0
位图在内核中,保持最小未被使用原则
#include "head.h"
int init_tcp(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket fail");return 1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind fail");return 1;}ret = listen(sockfd, 100);if (ret < 0){perror("lisen fail");return 1;}return sockfd;
}
int main(int argc, char const *argv[])
{int sockfd = init_tcp("192.168.1.138", 50002);if (sockfd < 0){return 1;}struct sockaddr_in cliaddr;int clilen = sizeof(cliaddr);int maxs;fd_set rdfds;fd_set tmprdfds;FD_ZERO(&rdfds);FD_SET(sockfd, &rdfds);int i = 0;maxs = sockfd;char buf[1024]={0};while (1){tmprdfds = rdfds;int cnt = select(maxs + 1, &tmprdfds, NULL, NULL, NULL);if (cnt < 0){perror("fail select");return 1;}if (FD_ISSET(sockfd, &tmprdfds)){int connfd = accept(sockfd,(struct sockaddr*)&cliaddr, &clilen);if (connfd < 0){perror("fail accept");return 1;}FD_SET(connfd, &rdfds);maxs = maxs > connfd ? maxs : connfd;}// for(i=sockfd;i<maxs+1;++i)// {// printf("%d\n",i);// }// sleep(3);for (i = sockfd + 1; i < maxs + 1; ++i){if (FD_ISSET(i, &tmprdfds)){memset(buf, 0, sizeof(buf));ssize_t size = recv(i, buf, sizeof(buf), 0);if (size < 0){perror("recv fail");continue;}if(0==size){printf("client offlink\n");return 1;}printf("[%s] [%d] : %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);strcat(buf, "------ok");size = send(i, buf, sizeof(buf), 0);if (size < 0){perror("fail send");continue;}} }}close(sockfd);return 0;
}