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){ //disconnect
close(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); //聘请一个迎宾的listen
struct 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介绍服务员socket
while(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 0
while (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);
}
#else
int 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, 5
if (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) { // disconnect
close(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集合里