多进程并发服务器
- 主进程监听:服务器在指定端口上监听客户端连接请求。
- 接受连接:当有新的客户端连接时,服务器接受该连接,并为其派生一个新的子进程。
- 进程处理:子进程独立运行,处理客户端的请求,直到客户端断开连接。子进程在处理过程中可以读取数据、进行处理,并返回结果。
- 进程终止:子进程处理完毕后终止,释放相关资源。
#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;
//开始监测集合中的io
int cnt = select(maxfd+1, &tmfds, NULL, NULL, NULL);
if(cnt < 0)
handle_error("select fail");
//根据返回值的结果区分处理不同的io
if(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;
}