TCP:建立连接,一对一
要实现多任务并发,就引出了并发模型
一、多进程与多线程
1.在相同资源情况下,进程资源开销大,但其安全性高
2.线程相对于进程资源开销小,且并发量比进程大
①多进程并发基础代码
cs
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
int init_tcp_http()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket error");
return -1;
}
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(51000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");
int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (ret < 0)
{
perror("bind error");
return -1;
}
ret = listen(sockfd, 10);
if (ret < 0)
{
perror("listen error");
return -1;
}
return sockfd;
}
int main(int argc, char const *argv[])
{
struct sockaddr_in cliaddr;
socklen_t clien = sizeof(cliaddr);
int sockfd = init_tcp_http();
if(sockfd < 0)
{
return -1;
}
while(1)
{
int connfd =accept(sockfd,(struct sockaddr *)&cliaddr,&clien);
if(connfd < 0)
{
perror("accept error");
return -1;
}
pid_t pid = fork();
if(pid > 0)
{
}
else if(pid == 0)
{
char buff[1024];
while(1)
{
memset(buff,0,sizeof(buff));
ssize_t cnt = recv(connfd,buff,sizeof(buff),0);
if(cnt < 0)
{
perror("recv error");
return -1;
}
else if(cnt == 0)
{
printf("[%s : %d], online\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
}
printf("[%s : %d] %s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);
strcat(buff,"----->ok");
cnt = send(connfd,buff,sizeof(buff),0);
if(cnt < 0)
{
perror("recv error");
return -1;
}
}
close(connfd);
}
}
close(sockfd);
return 0;
}
②多线程并发基础代码实现
cs
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
int init_tcp_http()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket error");
return -1;
}
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(51000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");
int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (ret < 0)
{
perror("bind error");
return -1;
}
ret = listen(sockfd, 10);
if (ret < 0)
{
perror("listen error");
return -1;
}
return sockfd;
}
void *task(void *arg)
{
int connfd = *(int *)arg;
char buff[1024] = {0};
while(1)
{
memset(buff,0,sizeof(buff));
ssize_t cnt = recv(connfd,buff,sizeof(buff),0);
if(cnt < 0)
{
perror("recv error");
close(connfd);
return NULL;
}
else if(cnt == 0)
{
printf("online\n");
break;
}
printf("buff = %s\n",buff);
strcat(buff,"----->ok");
cnt = send(connfd,buff,sizeof(buff),0);
if(cnt < 0)
{
perror("recv error");
close(connfd);
return NULL;
}
}
close(connfd);
}
int main(int argc, char const *argv[])
{
pthread_t tid;
struct sockaddr_in cliaddr;
socklen_t clien = sizeof(cliaddr);
int sockfd = init_tcp_http();
if(sockfd < 0)
{
return -1;
}
while(1)
{
int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clien);
if(connfd < 0)
{
perror("accept error");
return -1;
}
pthread_create(&tid,NULL,task,&connfd);
pthread_detach(tid);
}
close(sockfd);
return 0;
}
二、线程池
为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。

简单的例子:主线程是一个餐厅,餐厅有四个服务员(此线程),当客人到餐厅就餐时,服务员前去接待,当四个服务员都没有空闲时,客人进行排队,就好比一个队列(先进先出),当服务员空闲来,就去"队列"中接待客人(task)
三、IO多路复用
I-->O:fd
对多个文件描述符的读写可以复用一个进程。
在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测
阻塞IO模式:
1. 多个任务之间是同步的效果
1)select
2)poll
3)epoll

2.相应函数解释
1)select实现IO多路复用:
-
创建文件描述符集合 fd_set
-
添加关注的文件描述符到集合 FD_SET();
-
使用select传递集合表给内核,内核开始监测事件 select()
-
当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果
-
根据select返回的结果做不同的任务处理

位图为1024是因为文件描述符是0~1023(前三位是标准输出,标准输入,标准出错设备)
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
这是系统自带的一些带参宏
FD_CLR 是将当前套接字置零
FD_ISSET 检测套接字是否置为1
FD_SET 将定义的套接字插入文件描述符集合
FD_ZERO 将文件描述符全清零
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
功能:传递文件描述符结合表给内核并等待获取事件结果
参数:
nfds : 关注的最大文件描述符+1
readfds: 读事件的文件描述符集合
writefds: 写事件的文件描述符集合
exceptfds:其他事件的文件描述符集合
timeout: 设置select监测时的超时时间
NULL : 不设置超时时间(select一直阻塞等待)
返回值:
成功:返回内核监测的到达事件的个数
失败:-1
0 : 超时时间到达,但没有事件发生,则返回0