LINUX 入门 8
day10 20240507 耗时:90min 有点到倦怠期了
课程链接地址
第8章 TCP服务器
1 TCP服务器的介绍
开始讲服务器端,之前是客户端DNS+https请求
- 基础:网络编程
- 并发服务器:多客户端
- 一请求,一线程 veryold
- IO多路复用,epoll/select上一章讲过了
- TCP server百万级连接
借助netassist.exe
2 TCP并发网络网络编程 一请求一线程
tcp server类似酒店迎宾领过去,监听listen
server有对应客户端的连接有socket 连接类似服务员点菜
没懂好多用法
-
void *client_routine(void *arg){int clientfd = *(int *) arg; }
这是一个使用多线程处理客户端请求的例子。在这个例子中,每个客户端连接都会创建一个新的线程,并且通过
client_routine
函数进行处理。下面是对提供的代码片段的解释:
void *client_routine(void *arg)
是一个线程函数,用于处理单个客户端连接。int clientfd = *(int *) arg;
将传入参数arg
解引用为整数类型,并将其赋值给clientfd
变量。假设传入参数是一个指向整数类型的指针,即客户端套接字描述符。*(int *) arg;
它首先将arg
强制转换为指向整数的指针,然后使用解引用操作符*
获取该指针所指向的值。
你可以在该函数中编写适当的代码来处理客户端请求,例如读取和发送数据等操作。请注意,在每个线程内部需要负责释放相关资源并确保线程安全性。
步骤:
gcc -o tcp_server tcp_server.c -lpthread
./tcp_server 8888
服务器就起来了
打开netassist 改远程地址: 192.168.243.128:8888
有bug连上了,但是点发送以后收不到!!!
或者是bind error, 没调出来,可能那里打错了,老师的是可以的
-
起多个客户端send,如何取区分
sockfd无法解决,需要应用协议发的内容不同<fromid: xxxx>
-
缺点:不适合超多client,内存不够; 用epoll改
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>#include <errno.h>
#include <fcntl.h>#include <sys/epoll.h>
#include <unistd.h> //close要#define BUFFER_LENGTH 1024// 法一 一请求一线程
void *client_routine(void *arg){int clientfd = *(int *) arg;while(1){char buffer[BUFFER_LENGTH] = {0}; // 用于从客户端套接字中接收数据并将其存储到缓冲区中。clientfd:表示要接收数据的套接字文件描述符。buffer:表示接收数据的缓冲区,也就是存放接收到的数据的位置。BUFFER_LENGTH:表示期望接收的最大字节数,即缓冲区的大小。0:表示额外选项,通常设置为 0。int len = recv(clientfd, buffer, BUFFER_LENGTH, 0);if(len < 0){// 没数据,如果阻塞,就是一直等,返回-1// 在非阻塞 I/O 模式下,当没有可用数据时,recv() 函数可能返回 -1 并设置 errno 为 EAGAIN 或 EWOULDBLOCK。这表示当前没有数据可供接收,并且稍后可能会有更多数据可用。因此,这段代码的作用是检测 errno 是否等于 EAGAIN 或 EWOULDBLOCK,以判断是否需要继续等待更多数据的到达。if(errno == EAGAIN ||errno == EWOULDBLOCK){close(clientfd);break;}else if(len == 0){ //disconnectclose(clientfd);break;}else{printf("recv: %s, %d byte(s)\n",buffer, len);}}}
} // 1 socket创建
int main(int argc, char*argv[]){if(argc<2) {printf("param error\n");return -1;}int port = atoi(argv[1]);int sockfd = socket(AF_INET, SOCK_STREAM, 0); //聘请一个迎宾的listenstruct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY; //任意地址不确定// 0成功 1失败// 函数bind()将套接字与特定的IP地址和端口号进行绑定,以便后续接收来自该地址的连接请求。// 而listen()则表示开始监听连接请求,并指定最大允许等待连接队列的长度为5。这意味着服务器可以同时处理5个未处理的连接请求,超过这个数量的请求将被拒绝或排队等待处理。if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){perror("bind error");return -2;}if(listen(sockfd, 5) < 0){perror("listen error");return -3;} // 2迎宾sockfd 一直等着 为客户client介绍服务员socketwhile(1){struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len = sizeof(client_addr);// 使用accept函数接受来自服务器监听套接字 sockfd 的客户端连接请求,并将客户端的地址信息存储在名为 client_addr 的结构体中。int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len); // 法一 一请求一线程// 请求来了创建线程pthread_t thread_id;pthread_create(&thread_id, NULL, client_routine, &clientfd);}}
3 TCP并发网络编程io多路复用epoll 水平触发与边沿触发
没敲,就看了一下
-
what is epoll
超多clients 对server发request
检测到哪个client发了数据
- epoll_create
- epoll_ctl control管理 增删改
- epoll_wait() 多长时间去一次
#if 0while (1) {struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len = sizeof(client_addr);int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);pthread_t thread_id;pthread_create(&thread_id, NULL, client_routine, &clientfd);}#elseint epfd = epoll_create(1); struct epoll_event events[EPOLL_SIZE] = {0};struct epoll_event ev;ev.events = EPOLLIN; ev.data.fd = sockfd;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);while (1) {int nready = epoll_wait(epfd, events, EPOLL_SIZE, 5); // -1, 0, 5if (nready == -1) continue;int i = 0;for (i = 0;i < nready;i ++) {if (events[i].data.fd == sockfd) { // listen struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len = sizeof(client_addr);int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);ev.events = EPOLLIN | EPOLLET; ev.data.fd = clientfd;epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);} else {int clientfd = events[i].data.fd;char buffer[BUFFER_LENGTH] = {0};int len = recv(clientfd, buffer, BUFFER_LENGTH, 0);if (len < 0) {close(clientfd);ev.events = EPOLLIN; ev.data.fd = clientfd;epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);} else if (len == 0) { // disconnectclose(clientfd);ev.events = EPOLLIN; ev.data.fd = clientfd;epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);} else {printf("Recv: %s, %d byte(s)\n", buffer, len);}}}}#endif
一边听,敲,记笔记云里雾里的
- 关于io有无数据?如何检测 epoll_wait
- 有1 无0
- 一种是水平触发EPOLLIN:有没有;一种是边沿触发EPOLLET:检测从无变有
**面试重点:**epoll两种触发,reactor,协程,epoll 在sockfd set集合里