IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 网络高并发 -> 正文阅读

[网络协议]网络高并发

1.使用多进程并发处理多个client请求

网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端。

但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程。

如果每次连上一个客户端,都创建一个新进程,然后操作完了,就结束子进程,实在太麻烦,而且系统开销很大。

因此我们可以使用线程,相比于进程,要轻量级的多。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>

#define SERV_PORT 8000
#define MAXLINE 80

#define prrexit(msg) perror(msg); exit(1);

void *up_server(void *arg) {
    int connfd = (int)arg;
    char buf[MAXLINE];

    pthread_detach(pthread_self());
    //多线程
    pthread_t tid;
    pthread_create(&tid, NULL, up_server, (void *)connfd);
    printf("new thread is %#lx\n", tid);

    while (1) {
        int n = read(connfd, buf, MAXLINE);
        if (!strncmp(buf, "quit", 4)) break;
        write(1, buf, n);

        for (int i = 0; i < n; i++) {
            buf[i] = toupper(buf[i]);
        }
        write(connfd, buf, n);
    }
    close(connfd);
    return (void *)0;
}

int main(void) {
    struct sockaddr_in serveraddr, clientaddr;
    int listenfd, connfd;
    socklen_t clientaddr_len;

    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) prrexit("socket");

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERV_PORT);
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) prrexit("bind");
    if (listen(listenfd, 3) < 0) prrexit("listend");
    printf("Accepting connections...\n");
    while (1) {
        clientaddr_len = sizeof(clientaddr);
        connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddr_len);
        if (connfd < 0) prrexit("accept");

        printf("Received from %s:%d\n", inet_ntop(AF_INET, &clientaddr.sin_addr, str, sizeof(str)), ntohs(clientaddr.sin_port));

        /*pid_t pid = fork();
        if (pid < 0) prrexit("fork");
        if (pid > 0) {
            close(connfd);

            while (waitpid(-1, NULL, WNOHANG) > 0) {}

            continue;
        }

        close(listenfd);*/
        while (1) {
            int n = read(connfd, buf, MAXLINE);
            if (!strncmp(buf, "quit", 4)) break;
            write(1, buf, n);

            for (int i = 0; i < n; i++) {
                buf[i] = toupper(buf[i]);
            }
            write(connfd, buf, n);
        }
        close(connfd);
    }
    return 0;
}

2.基于UDP协议的网络程序

UDP客户端/服务器通讯过程:

server:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>

#define SERV_PORT 8000
#define MAXLINE 80

#define prrexit(msg) perror(msg); exit(1);

int main(void) {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    printf("udpserver ready~\n");

    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    socklen_t cliaddr_len;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    while (1) {
        int n = recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr *)&cliaddr, &cliaddr_len);
        if (n < 0) prrexit("recvfrom");
        printf("receive from %s:%d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));

        for (int i = 0; i < n; i++) {
            buf[i] = toupper(buf[i]);
        }
        sendto(sockfd, buf, MAXLINE, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    }
    close(sockfd);
    return 0;
}

client:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <string.h>

#define SERV_PORT 8000
#define MAXLINE 80

int main(void) {
    struct sockaddr_in servaddr;

    char buf[MAXLINE];
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

    int n;
    while(n = read(sockfd, buf, MAXLINE)) {
        n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
        n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
        write(1, buf, n);
    }
    close(sockfd);
    return 0;
}

3.使用线程池并发处理

可以创建很多线程池竞争得到客户端服务任务并之执行操作。

线程池:所有线程去抢夺资源。

4.使用epoll+线程池并发处理多个

epoll:通过注册callback函数方式,当某个文件描述符发送变化时,就会主动通知。

server:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>
#include <sys/epoll.h>

#define SERV_PORT 8000
#define MAXLINE 80

#define prrexit(msg) perror(msg); exit(1);

typedef struct Task
{
    int fd;
    struct Task *next;
}Task;

typedef struct Task_pool {
    Task *head;
    Task *tail;
    pthread_mutex_t lock;
    pthread_cond_t havetask;
}Task_pool;

Task_pool *task_pool_init() {
    Task_pool *tp = (Task_pool *)malloc(sizeof(Task_pool));
    tp->head = NULL;
    tp->tail = NULL;
    pthread_mutex_init(&tp->lock, NULL);
    pthread_cond_init(&tp->havetask, NULL);

    return tp;
}

void task_pool_push(Task_pool *tp, int fd) {
    pthread_mutex_lock(&tp->lock);

    Task *t = (Task *)malloc(sizeof(Task));
    t->fd = fd;
    t->next = NULL;

    if (!tp->tail) {
        tp->head = tp->tail = t;
    } else {
        tp->tail->next = t;
        tp->tail = t;
    }

    pthread_cond_broadcast(&tp->havetask);
    pthread_mutex_unlock(&tp->lock);
}

Task task_pool_pop(Task_pool *tp) {
    pthread_mutex_lock(&tp->lock);
    while (tp->head == NULL) {
        pthread_cond_wait(&tp->havetask, &tp->lock);
    }
    Task tmp, *k;
    k = tp->head;
    tmp = *k;
    tp->head = tp->head->next;

    if (!tp->head) tp->tail = NULL;
    free(k);
    pthread_mutex_unlock(&tp->lock);
    return tmp;
}

void task_pool_free(Task_pool *tp) {
    pthread_mutex_lock(&tp->lock);
    Task *p = tp->head, *k;
    while (p) {
        k = p;
        p = p->next;
        free(k);
    }
    tp->head = NULL;
    pthread_mutex_unlock(&tp->lock);
    pthread_mutex_destroy(&tp->lock);
    pthread_cond_destroy(&tp->havetask);

    free(tp);
    return;
}

void *up_server(void *arg) {
    pthread_detach(pthread_self());
    char buf[MAXLINE];
    int n, i;
    Task_pool *tp = arg;

    while (1) {
        Task tmp = task_pool_pop(tp);
        int connfd = tmp.fd;
        printf("get task fd=%d\n", connfd);
        if (1) {
            n = read(connfd, buf, MAXLINE);
            write(1, buf, n);

            for (i = 0; i <n; i++) {
                buf[i] = toupper(buf[i]);
            }
            write(connfd, buf, n);
        }
        printf("finish task fd=%d\n", connfd);
        if (!strncmp(buf, "quit", 4)) {
            close(connfd);
        }
    }
    return (void *)0;
}


int main(void) {
    int sockfd;
    int listenfd, connfd;
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;

    Task_pool *tp = task_pool_init();
    //多线程
    pthread_t tid;
    for (int i = 0; i < 4; i++) {
        pthread_create(&tid, NULL, up_server, (void *)tp);
        printf("new thread is %#lx\n", tid);
    }

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) prrexit("socket");

    int epfd = epoll_create(256);
    struct epoll_event ev, events[256];


    //服务器IP地址:端口初始化
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) prrexit("bind");

    if (listen(listenfd, 2) < 0) prrexit("listen");

    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];

    printf("Accepting connections...\n");
    while (1) {
        int nfds = epoll_wait(epfd, events, 256, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == listenfd) {
                cliaddr_len = sizeof(cliaddr);
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
                if (connfd < 0) prrexit("accept");
               
                printf("receive from %s:%d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));

                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = listenfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
                //task_pool_push(tp, connfd);
            } else if (events[i].events & EPOLLIN) {
                int clifd = events[i].data.fd;
                if (clifd < 3) continue;
                task_pool_push(tp, clifd);
            }
        }
    }

    task_pool_free(tp);
    return 0;
}

5.HTTP协议

超文本传输协议(HyperText Trasnsfer Protocol)是一种用于分布式、协作式和超媒体信息系统的应用层协议。

HTTP是万维网的数据通信的基础。

①HTTP协议采用了请求/响应模型。

②无状态保存

HTTP是一种不保存状态,无状态协议。

HTTP协议自身不对请求和响应之间的通信状态进行报错。在HTTP这个级别,协议对于发送过的请求或响应都不做持久化处理。

③无连接

限制每次连接只处理一个请求。

服务器处理完客户的请求,并收到客户的应答后,即断开连接。

采用这种方式可节省传输时间并提高并发性能,不能和每个用户建立长久连接,请求一次响应一次,服务端和客户端就中断了。

6.HTTP请求方法

HTTP/1.1协议中共定义8种方法(动作)来以不同方式操作指定的资源:

①GET

向指定的资源发出“显示”请求。

②POST

向指定资源提交数据,请求服务器进行处理(如:提交表单或者上传文件)。

③HEAD

与GET一样,都是向服务器发出指定资源的请求。但服务器将不传回资源的文本部分。

④PUT

向指定资源位置上传其最新内容。

⑤DELETE

请求服务器删除Request-URI所标识的资源。

⑥TRACE

回显服务器收到的请求,主要用于测试或诊断。

⑦OPTIONS

可使服务器传回该资源所支持的所有HTTP请求方法。

⑧CONNECT

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

7.HTTP状态码

所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码以及描述状态的短语,彼此由空格分隔。

8.URL

超文本传输协议HTTP的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:

9.HTTP请求格式

10.执行CGI程序

如果浏览器请求的是一个可执行文件(不管是什么样的可执行文件,即使shell脚本也一样),服务器并不把这个文件本身发给浏览器,而是把它执行结果标准输出发给浏览器。

web浏览器工作流程:

①解析浏览器的请求,在服务目录中查找相应文件,找不到该文件就返回404错误页面

②如果找到浏览器请求的文件,用stat(2)检查它是否可执行

③如果该文件可执行:

a.发送HTTP/1.1 200 OK给客户端

b.fork(2),用dup2(2)重定向子进程的标准输出到客户端socket

c.在子进程中exec(3)该CGI程序

d.关闭连接

④如果改文件不可执行:

a.发送HTTP/1.1 200 OK给客户端

b.如果是一个图片文件,根据图片的扩展名发送相应的Content-type

c.如果不是图片文件,做简化处理,都当作Content-Type:text

d.简单的HTTP协议头有这两行就足够,再发一个空行表示结束

e.读取文件的内容发送到客户端

f.关闭连接

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:30:42  更:2022-02-16 13:30:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:01:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码