通过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. epfd:epoll_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;
}
}
}
}
}