HTTP通信与多线程服务器实战

通过ping 获得地址IP,可以访问到他

http 通信过程:

1,建立连接

2,http-----按照他的协议进行收和发。

(1)请求报文

(2)响应报文

注,URL 统一资源定位符。

查询实时自选城市的实时天气:

顺序:

先抓取对应实时天气的报文------------红色部分;

用代码建立和实时天气对应的地址的联系---------运行即可

注意:

1,weaid-----i 小写

2,最后一行两个 \r\n

响应报文:

函数:strstr

服务器是如何实现----------同时处理多个客户端?

并发服务器-----支持多个客户端同时访问。

之前的客户端只能支持单个客户端的操作。

迭代服务器

多线程:

相对于进程创建和调度开销小------共享资源方便--------线程之间进程和同步问题

bash 复制代码
#include <stdio.h>
#include <sys/types.h>	       /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>

void * do_client (void *arg)
{
	
	int connfd = *(int *)arg;
	//通信 
	char buf[1024];
	while (1)
	{
		read(connfd,buf,sizeof(buf));
		printf("buf = %s\n",buf);
		if (strncmp(buf,"quit",4) == 0)
		{
			close(connfd);
			break;
		}
	}

	return NULL;
}

int server_init(const char *ip,unsigned short port)
{
	//1. socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if (fd < 0)
	{
		perror("socket fail");
		return -1; 
	}
	//2. bind  
	struct sockaddr_in addr; //结构体 
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	//	addr.sin_addr.s_addr = inet_addr("172.26.54.170");
	addr.sin_addr.s_addr = inet_addr(ip);
	addr.sin_port = htons(port);
	if (bind(fd,(const struct sockaddr*)&addr,sizeof(addr))  < 0)
	{
		perror("bind fail");
		return -1;
	}

	//3.listen
	if (listen(fd,5) < 0)
	{
		perror("listen fail");
		return -1;
	}

	return fd;
}

//tcp 客户端 
int main(int argc, const char *argv[])
{

	//1.链接 
	int fd = server_init("127.0.0.1",50001);
	if (fd < 0)
	{
		printf("server_init fail\n");
		return -1;
	}
	//2.通信 
	struct sockaddr_in cliaddr;
	bzero(&cliaddr,sizeof(cliaddr));
	socklen_t len = sizeof(cliaddr);
	while (1)
	{
		//4.accept 
		int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len);
		if (connfd < 0)
		{
			perror("accept fail");
			return -1;
		}
		printf("connfd = %d\n",connfd);
		printf("--------------------\n");
		printf("client ip = %s\n",inet_ntoa(cliaddr.sin_addr));
		printf("client port = %d\n",ntohs(cliaddr.sin_port));

		pthread_t tid;
		int ret = pthread_create(&tid,NULL,do_client,&connfd);
		if (ret != 0)
		{
			errno = ret;
			perror("pthread_create fail");
			return -1;
		}
		pthread_detach(tid); //设置成分离 
	} 
	return 0;
}
多进程:
bash 复制代码
#include <stdio.h>
#include <sys/types.h>	       /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>

void do_handler(int signo)
{
	wait(NULL);
}

int server_init(const char *ip,unsigned short port)
{
	//1. socket
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if (fd < 0)
	{
		perror("socket fail");
		return -1; 
	}
	//2. bind  
	struct sockaddr_in addr; //结构体 
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	//	addr.sin_addr.s_addr = inet_addr("172.26.54.170");
	addr.sin_addr.s_addr = inet_addr(ip);
	addr.sin_port = htons(port);
	if (bind(fd,(const struct sockaddr*)&addr,sizeof(addr))  < 0)
	{
		perror("bind fail");
		return -1;
	}
	//3.listen
	if (listen(fd,5) < 0)
	{
		perror("listen fail");
		return -1;
	}

	return fd;
}

//tcp 客户端 
int main(int argc, const char *argv[])
{

	//1.链接 
	int fd = server_init("127.0.0.1",50001);
	if (fd < 0)
	{
		printf("server_init fail\n");
		return -1;
	}
	//2.通信 
	struct sockaddr_in cliaddr;
	bzero(&cliaddr,sizeof(cliaddr));
	socklen_t len = sizeof(cliaddr);

	signal(SIGCHLD,do_handler);
	while (1)
	{
		//4.accept 
		int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len);
		if (connfd < 0)
		{
			perror("accept fail");
			return -1;
		}
		printf("connfd = %d\n",connfd);
		printf("--------------------\n");
		printf("client ip = %s\n",inet_ntoa(cliaddr.sin_addr));
		printf("client port = %d\n",ntohs(cliaddr.sin_port));

		pid_t pid = fork();
		if (pid < 0)
		{
			perror("fork fail");
			return -1;
		}

		if (pid > 0)
		{
			continue;
		}else if (pid == 0)
		{
			//通信 
			char buf[1024];
			while (1)
			{
				read(connfd,buf,sizeof(buf));
				printf("buf = %s\n",buf);
				if (strncmp(buf,"quit",4) == 0)
				{
					close(connfd);
					exit(0);
				}
			}
		}
	} 
	return 0;
}

服务器模型:

IO多路复用

// 核心目的------提高并发程度---当前进程 有多处 输入和输出。

阻塞IO:实现简单------效率不太高

非阻塞IO:当去内核读取数据的时候,如果没数据,不等待,即刻返回。

需要轮询操作。-----搭配y一直去读:while(1)

非常耗费CPU;

定义非阻塞的状态

方法1:非阻塞打开文件

方法2:

打开文件后:

int flag = fcntl (fd,F_GETFL,0) 获取默认状态

flag = flag | O_NONBLOCK 定义非阻塞

fcntl(fd,F_SETFL,flag) 使用新定义的状态

IO多路的复用:

select

1,用函数 select 去监控多路IO-------有数据的消息就给提醒,没有就不提醒

功能:监控多路文件描述符,看是否有就绪的

参数:

nfds---------要监控的文件描述符最大值加一

readfds ------- 是否可读

writefds-------是否可写

exceptfds-------是否出错

timeout--- --------0 表示select,非阻塞-------->0 阻塞------------NULL默认阻塞

返回值:

返回文件数量 &&失败-1

1,准备监控表:

fd_set readfds

2,添加文件描述符

注意select函数调用结束后,会返回调用的文件描述符,而没被使用的文件描述符,则不会被使用。

select主要问题

1,文件描述符受限

2,效率不高

3。返回就绪的个数,需要进一步处理

监控epoll:

创建

epoll_creat( ) // 最少写1

epoll_ctl( )

ev.events = EPOLLIN/EPOLLET(检测边沿触发-----只关心发送的数据从无到有的一顺间)

epoll ctl(DEL/ADD)

ET监听的时候,fd最好是非阻塞的方式。(高并发)

epoll+et+线程;

函数名 核心作用 简单用法 & 关键说明
epoll_create(int size) 创建一个 epoll 实例(句柄),用于管理需要监听的文件描述符 1. size:早期版本需指定监听的最大数量,现在可填任意正数(如 1024),内核已不依赖此值;2. 返回值:成功返回 epoll 句柄(文件描述符),失败返回 -1;3. 类比:相当于创建一个 "监听容器",用来装要监控的 fd。
epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 管理 epoll 实例中的监听列表(添加 / 修改 / 删除要监听的文件描述符) 1. epfdepoll_create 返回的 epoll 句柄;2. op:操作类型,常用 3 种: - EPOLL_CTL_ADD:添加 fd 到监听列表; - EPOLL_CTL_MOD:修改 fd 的监听事件; - EPOLL_CTL_DEL:从监听列表删除 fd;3. fd:要监听的文件描述符(如 socket、管道);4. event:指定监听的事件(如 EPOLLIN 读事件、EPOLLOUT 写事件)。
epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) 等待 epoll 实例中监听的 fd 发生事件,获取就绪的 fd 列表 1. epfd:epoll 句柄;2. events:输出参数,用于存放就绪的事件(fd + 事件类型);3. maxevents:最多能接收的就绪事件数(需 ≤ epoll_create 的 size);4. timeout:超时时间(毫秒): - 0:立即返回,不阻塞; - -1:一直阻塞,直到有事件发生; - >0:阻塞指定毫秒后超时返回;5. 返回值:成功返回就绪的 fd 数量,0 表示超时,-1 表示失败。
bash 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/epoll.h>


int epoll_add(int epfd,int fd)
{
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = fd;

	if (epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) < 0)
	{
		perror("epoll_ctl fail");
		return -1;
	}
	return 0;
}

int main(int argc, const char *argv[])
{

	if (argc != 2)
	{
		printf("Usage: %s <fifo file>\n",argv[0]);
		return -1;
	}

	if (mkfifo(argv[1],0666) < 0 && errno!= EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}

	printf("success\n");

	//1.打开文件 
    int fd = open(argv[1],O_RDONLY);
    //int fd = open(argv[1],O_RDONLY|O_NONBLOCK);
	if (fd < 0)
	{
		perror("open fail");
		return -1;
	}
	printf("read ---- \n");


	//1.创建一个epoll对象 
	int epfd = epoll_create(1);
	if (epfd < 0)
	{
		perror("epoll_create fail");
		return -1;
	}

	//2.添加关心的事件 
	epoll_add(epfd,fd);
	epoll_add(epfd,0); 


	while (1)
	{
	//3.监控
	
		struct epoll_event rev[2];
		int ret = epoll_wait(epfd,rev,2,-1);

		if (ret > 0)
		{
			int i = 0;
			for (i = 0; i < ret; ++i)
			{
				if (rev[i].data.fd == fd)
				{
						//2.写
						char buf[1024] = {0};
						int ret = read(fd,buf,sizeof(buf));
						printf("fifo ret = %d buf = %s\n",ret,buf);


						if (strncmp(buf,"quit",4) == 0)
						{
							break;
						}

				}else if (rev[i].data.fd == 0)
				{
						char buf[1024] = {0};
						int ret = read(0,buf,sizeof(buf));
						printf("stdin ret = %d buf = %s\n",ret,buf);

						if (strncmp(buf,"quit",4) == 0)
						{
							break;
						}

				}
			}

		}
	}
	
相关推荐
雨落在了我的手上2 小时前
C语言之数据结构初见篇(2):顺序表之通讯录的实现(续)
c语言·开发语言·数据结构
码不停蹄Zzz3 小时前
对内存堆栈管理的简单理解[C语言]
c语言·开发语言
AMoon丶5 小时前
C++基础-类、对象
java·linux·服务器·c语言·开发语言·jvm·c++
为搬砖记录5 小时前
杰理AC695N soundbox 3.1.2打开ble宏的编译bug
c语言·开发语言·单片机·bug
一叶落4385 小时前
【LeetCode 172】阶乘后的零(C语言详解 | 数学规律 + 对数时间复杂度)
c语言·数据结构·算法·leetcode·动态规划
自信150413057595 小时前
数据结构初阶——二叉树之——堆的实现
c语言·数据结构·算法
小茗的嵌入式学习日记6 小时前
基于IMX6ULL的车载中控系统
linux·c语言·qt
香水5只用六神6 小时前
【RTOS快速入门】05_动态_静态创建任务(2)
c语言·stm32·单片机·嵌入式硬件·freertos·rtos·嵌入式软件
香水5只用六神6 小时前
【RTOS快速入门】06_任务状态理论讲解(1)
c语言·stm32·单片机·嵌入式硬件·freertos·rtos·嵌入式软件