多进程并发服务器
- 主进程监听:服务器在指定端口上监听客户端连接请求。
- 接受连接:当有新的客户端连接时,服务器接受该连接,并为其派生一个新的子进程。
- 进程处理:子进程独立运行,处理客户端的请求,直到客户端断开连接。子进程在处理过程中可以读取数据、进行处理,并返回结果。
- 进程终止:子进程处理完毕后终止,释放相关资源。
#include <head.h>int init_tcp_ser(const char *ip ,unsigned short port)
{ int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){perror("fail socket");return -1;}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;
}void handler(int signo)
{wait(NULL);
}int main(int argc, const char *argv[])
{pid_t pid = 0;int connfd = 0;char buff[1024] = {0};signal(SIGCHLD, handler);int sockfd = init_tcp_ser("192.168.1.100", 50000);if (-1 == sockfd){return -1;}while (1){connfd = accept(sockfd, NULL, NULL);if (-1 == connfd){perror("fail accept");return -1;}pid = fork();if (pid > 0){}else if (0 == pid){while (1){memset(buff, 0, sizeof(buff));ssize_t size = recv(connfd, buff, sizeof(buff), 0);if (size <= 0){break;}printf("cli---> %s\n", buff);strcat(buff, "--->ok!");send(connfd, buff, strlen(buff), 0);}close(connfd);}}return 0;
}
多线程并发服务器
- 主线程监听:服务器在指定端口上监听客户端连接请求。
- 接受连接:当有新的客户端连接时,服务器接受该连接,并为其创建一个新的线程。
- 线程处理:新线程负责处理该客户端的所有请求,直到客户端断开连接。线程可以读取客户端发送的数据、进行处理,并将结果发送回客户端。
- 线程终止:在处理完毕后,线程可以选择继续等待新的请求(长连接)或终止(短连接)。
#include <head.h>int init_tcp_ser(const char *ip ,unsigned short port)
{ int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){perror("fail socket");return -1;}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;
}void *do_communicate(void *arg)
{int connfd = *((int *)arg);char buff[1024] = {0};while (1){ssize_t size = recv(connfd, buff, sizeof(buff), 0);if (size <= 0){break;}printf("cli---> %s\n", buff);strcat(buff, "----->ok!");send(connfd, buff, strlen(buff), 0);}close(connfd);return NULL;
}int main(int argc, const char *argv[])
{pid_t pid = 0;int connfd = 0;pthread_t tid;char buff[1024] = {0};int sockfd = init_tcp_ser("192.168.1.163", 50000);if (-1 == sockfd){return -1;}while (1){connfd = accept(sockfd, NULL, NULL);if (-1 == connfd){perror("fail accept");return -1;}pthread_create(&tid, NULL, do_communicate, &connfd);pthread_detach(tid);}return 0;
}
IO模型
1.阻塞IO
#include <head.h>
int main(int argc, char *argv[])
{char buf[1024] = {0};mkfifo("./myfifo",0664);int fifofd = open("./myfifo",O_RDONLY);if(-1 == fifofd)handle_error("open fail");while(1){fgets(buf,sizeof(buf),stdin);printf("stdin: %s\n",buf);memset(buf,0,sizeof(buf));read(fifofd,buf,sizeof(buf));printf("fifo: %s\n",buf);}close(fifofd);return 0;
}
2.非阻塞IO
fcntl
int fcntl(int fd, int cmd, ... /* arg */ );
(1)功能:设置文件描述符属性
(2)参数:
- fd:文件描述符
- cmd:F_GETFL 获得文件描述符属性
- F_SETFL 设置文件描述符属性
(3)返回值:
- F_GETFL:成功返回获得的属性;失败返回-1
- F_SETFL:成功返回0;失败返回-1
O_NONBLOCK 非阻塞;O_ASYNC 异步方式
#include <head.h>
int main(int argc, char *argv[])
{char buf[1024] = {0};mkfifo("./myfifo",0664);int fifofd = open("./myfifo",O_RDONLY);if(-1 == fifofd)handle_error("open fail");//获取文件原有属性int flag = fcntl(0,F_GETFL);//增加非阻塞属性 flag = flag | O_NONBLOCK;//去掉非阻塞属性//flag = flag & (~O_NONBLOCK);//设置属性fcntl(0,F_SETFL,flag);while(1){memset(buf,0,sizeof(buf));fgets(buf,sizeof(buf),stdin);printf("stdin: %s\n",buf);memset(buf,0,sizeof(buf));read(fifofd,buf,sizeof(buf));printf("fifo: %s\n",buf);}close(fifofd);return 0;
}
3.信号驱动IO
#include <head.h>void handle(int signo)
{char buf[1024] = {0};fgets(buf,sizeof(buf),stdin);printf("stdin: %s\n",buf);
}
int main(int argc, char *argv[])
{signal(SIGIO,handle);char buf[1024] = {0};mkfifo("./myfifo",0664);int fifofd = open("./myfifo",O_RDONLY);if(-1 == fifofd)handle_error("open fail");//获取文件原有属性int flag = fcntl(0,F_GETFL);flag = flag | O_ASYNC;fcntl(0,F_SETFL,flag);//关联当前进程fcntl(0,F_SETOWN,getpid());while(1){memset(buf,0,sizeof(buf));read(fifofd,buf,sizeof(buf));printf("fifo: %s\n",buf);}close(fifofd);return 0;
}
4.多路复用IO
1.select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
(1)功能:监听文件描述符集合
(2)参数:
- nfds:监测的文件描述符上限值(最大文件描述符的值+1)
- readfds:读文件描述符集合
- writefds:写文件描述符集合
- exceptfds:异常条件的描述符集合
- timeout:设置超时时间;NULL:一直等待
(3)返回值:
- 成功返回产生事件文件描述符个数
- 失败返回-1
- 定时时间到达仍没有事件产生返回0
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);
文件描述符集合清0
#include <head.h>
int main(int argc, char *argv[])
{int maxfd = 0;char buf[1024] = {0};mkfifo("./myfifo",0664);int fifofd = open("./myfifo",O_RDONLY);if(-1 == fifofd)handle_error("open fail");//创建文件描述符集合fd_set rdfds;fd_set tmfds;//清空集合FD_ZERO(&rdfds);//添加关注的文件描述符到集合中FD_SET(0,&rdfds);maxfd = 0 > maxfd ? 0 : maxfd;FD_SET(fifofd,&rdfds);maxfd = fifofd > maxfd ? fifofd : maxfd;while(1){tmfds = rdfds;//开始监测集合中的ioint cnt = select(maxfd+1, &tmfds, NULL, NULL, NULL);if(cnt < 0)handle_error("select fail");//根据返回值的结果区分处理不同的ioif(FD_ISSET(0, &tmfds)){memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), stdin);printf("stdin: %s\n",buf);}if(FD_ISSET(fifofd, &tmfds)){memset(buf,0,sizeof(buf));read(fifofd,buf,sizeof(buf));printf("fifo: %s\n",buf);}}close(fifofd);return 0;
}
2.poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
(1)功能:监听文件描述符集合中的事件
(2)参数:
- ds:文件描述符集合事件数组首地址
- nfds:事件个数
- timeout:超时时间
(3)返回值:
- 成功返回产生事件的文件描述符个数
- 失败返回-1
- 超时时间到达仍没有产生事件返回0
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */};
3.epoll模型
(1)epoll_create
int epoll_create(int size);
1)功能:创建一个监听事件表(内核中)
2)参数:size:监听事件最大个数
3)返回值:成功返回非负值:表示epoll事件表对象(句柄);失败返回-1
(2)epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
1)功能:在监听事件表中新增一个事件
2)参数:
epfd:事件表文件描述符
op: EPOLL_CTL_ADD 新增事件
EPOLL_CTL_MOD 修改事件
EPOLL_CTL_DEL 删除事件
fd:文件描述符
event:EPOLLIN 读事件; EPOLLOUT 写事件
struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */};
3)返回值:成功返回0 ;失败返回-1
(3)epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
1)功能:监听事件表中的事件,并将产生的事件存放到结构体数组中
2)参数:
- epfd:事件表文件描述符
- events:存放结果事件结构体数组空间首地址
- maxevents:最多存放事件个数
- timeout:超时时间; -1:阻塞等待直到有事件发生
3)返回值:
- 成功返回产生事件个数
- 失败返回-1
- 超时时间到达没有事件发生返回0
#include <head.h>
#include <sys/epoll.h>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)handle_error("epoll_ctl fail");return 0;
}
int epoll_del_fd(int epfds, int fd)
{int ret = epoll_ctl(epfds, EPOLL_CTL_DEL, fd, NULL);if(-1 == ret)handle_error("epoll_ctl fail");return 0;
}int main(int argc, char *argv[])
{int maxfd = 0;char buff[1024] = {0};mkfifo("./myfifo",0664);int fifofd = open("./myfifo", O_RDONLY);if(-1 == fifofd)handle_error("open fail");struct epoll_event evs[2];int epfds = epoll_create(2);if(-1 == epfds)handle_error("epoll_create fail");epoll_add_fd(epfds, 0, EPOLLIN);epoll_add_fd(epfds, fifofd, EPOLLIN);while(1){int cnt = epoll_wait(epfds, evs, 2, -1);if(cnt < 0)handle_error("epoll_wait fail");for(int i = 0; i < cnt; i++){if(0 == evs[i].data.fd){memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);printf("STDIN: %s\n", buff);}else if(fifofd == evs[i].data.fd){memset(buff, 0, sizeof(buff));ssize_t size = read(evs[i].data.fd, buff, sizeof(buff));if(size < 0){epoll_del_fd(epfds, evs[i].data.fd);close(evs[i].data.fd);continue;}printf("FIFO: %s\n", buff);}}}return 0;
}