c++网络编程下Linux的epoll技术和Windows下的IOCP模型

目录
  • 一、IOCP和Epoll之间的异同
    • 1、异
    • 2、同
  • 二:Epoll理解与应用。
    • 1、epoll是什么?
    • 2、epoll与select对比优化
    • 3、epoll是怎么优化select问题的
  • 三、epoll的几个函数的介绍:
    • 1、epoll_create函数
    • 2、epoll_ctl函数
    • 3、epoll_wait函数
    • 4、条件触发和边缘触发
  • 四、IOCP理解与应用
    • 1、传统服务器的网络IO流程
    • 2、使用IOCP的基本步骤

一、IOCP和Epoll之间的异同

1、异

1).IOCP是WINDOWS系统下使用。Epoll是Linux系统下使用。
2).IOCP是IO操作完毕之后,通过Get函数获得一个完成的事件通知。
Epoll是当你希望进行一个IO操作时,向Epoll查询是否可读或可写,若处于可读或可写状态,Epoll会通过epoll_wait进行通知。
3).IOCP封装了异步的消息事件的通知机制,同时封装了部分IO操作。但Epoll仅仅封装了一个异步事件的通知机制,并不负责IO读写操作。Epoll保持了事件通知和IO操作间的独立性,更加简单灵活。
4).基于上面的描述,我们可以知道Epoll不负责IO操作,所以它只告诉你当前可读可写了,并且将协议读写缓冲填充,由用户去读写控制,此时我们可以做出额外的许多操作。IOCP则直接将IO通道里的读写操作都做完了才通知用户,当IO通道里发生了堵塞等状况我们是无法控制的。

2、同

1).它们都是异步的事件驱动的网络模型。
2).它们都可以向底层进行指针数据传递,当返回事件时,除可通知事件类型外,还可以通知事件相关数据。

二:Epoll理解与应用。

1、epoll是什么?

epoll是当前在Linux下开发大规模并发网络程序的热门人选,epoll 在Linux2.6内核中正式引入,和select相似,都是I/O多路复用(IO multiplexing)技术。

Linux下设计并发网络程序,常用的模型有:
Apache模型(Process Per Connection,简称PPC)
TPC(Thread PerConnection)模型
select模型和poll模型。
epoll模型

2、epoll与select对比优化

基于select的I/O复用技术速度慢的原因:
1),调用select函数后常见的针对所有文件描述符的循环语句。它每次事件发生需要遍历所有文件描述符,找出发生变化的文件描述符。(以前写的示例没加循环)
2),每次调用select函数时都需要向该函数传递监视对象信息。即每次调用select函数时向操作系统传递监视对象信息,至于为什么要传?是因为我们监视的套接字变化的函数,而套接字是操作系统管理的。(这个才是最耗效率的)

注释:基于这样的原因并不是说select就没用了,在这样的情况下就适合选用select:1,服务端接入者少 2,程序应具有兼容性。

3、epoll是怎么优化select问题的

1),每次发生事件它不需要循环遍历所有文件描述符,它把发生变化的文件描述符单独集中到了一起。
2),仅向操作系统传递1次监视对象信息,监视范围或内容发生变化时只通知发生变化的事项。

实现epoll时必要的函数和结构体:

函数:
epoll_create:创建保存epoll文件描述符的空间,该函数也会返回文件描述符,所以终止时,也要调用close函数。(创建内存空间)
epoll_ctl:向空间注册,添加或修改文件描述符。(注册监听事件)
epoll_wait:与select函数类似,等待文件描述符发生变化。(监听事件回调)

结构体:

struct epoll_event
{
__uint32_t events;
epoll_data_t data;
}

typedef union epoll_data
{
void *ptr;
int fd;
__uinit32_t u32;
__uint64_t u64;
} epoll_data_t;

三、epoll的几个函数的介绍:

1、epoll_create函数

/**
 * @brief    该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。
 *
 * @param    size    size就是你在这个epoll fd上能关注的最大socket fd数
 *
 * @return   生成的文件描述符
 */
int epoll_create(int size);

2、epoll_ctl函数

/**
 * @brief    该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
 *
 * @param    epfd    由 epoll_create 生成的epoll专用的文件描述符
 * @param    op      要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
 * @param    fd      关联的文件描述符
 * @param    event   指向epoll_event的指针
 *
 * @return   0       succ
 *           -1      fail
 */  

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

其中用到的数据结构结构如下
op值:

  • EPOLL_CTL_ADD:注册新的fd到epfd中;
  • EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
  • EPOLL_CTL_DEL:从epfd中删除一个fd;
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

常用的事件类型:

  • EPOLLIN :表示对应的文件描述符可以读;
  • EPOLLOUT:表示对应的文件描述符可以写;
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读
  • EPOLLERR:表示对应的文件描述符发生错误;
  • EPOLLHUP:表示对应的文件描述符被挂断;
  • EPOLLET: 表示对应的文件描述符有事件发生;

例:

struct epoll_event ev;
    //设置与要处理的事件相关的文件描述符
    ev.data.fd=listenfd;
    //设置要处理的事件类型
    ev.events=EPOLLIN|EPOLLET;
    //注册epoll事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 

3、epoll_wait函数

/**
 * @brief    该函数用于轮询I/O事件的发生
 *
 * @param    epfd        由epoll_create 生成的epoll专用的文件描述符
 * @param    events      用于回传代处理事件的数组
 * @param    maxevents   每次能处理的事件数
 * @param    timeout     等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
 *
 * @return   >=0         返回发生事件数
 *           -1          错误
 */  

int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);

用改良的epoll实现回声服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *buf);

int main(int argc, const char * argv[]) {
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t adr_sz;
    int str_len, i;
    char buf[BUF_SIZE];

    //类似select的fd_set变量查看监视对象的状态变化,epoll_event结构体将发生变化的文件描述符单独集中到一起
    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd, event_cnt;

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

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if(serv_sock == -1)
        error_handling("socket() error");

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if(bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)
        error_handling("bind() error");

    if(listen(serv_sock, 5) == -1)
        error_handling("listen() error");

    //创建文件描述符的保存空间称为“epoll例程”
    epfd = epoll_create(EPOLL_SIZE);
    ep_events = malloc(sizeof(struct epoll_event) *EPOLL_SIZE);

    //添加读取事件的监视(注册事件)
    event.events = EPOLLIN;  //读取数据事件
    event.data.fd = serv_sock;
    epoll_ctl(epdf, EPOLL_CTL_ADD, serv_sock, &event);

    while (1)
    {
        //响应事件,返回发生事件的文件描述符数
        event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);  //传-1时,一直等待直到事件发生
        if(event_cnt == -1)
        {
            puts("epoll_wait() error");
            break;
        }

        //服务端套接字和客服端套接字
        for (i = 0; i < event_cnt; i++) {
            if(ep_events[i].data.fd == serv_sock)//服务端与客服端建立连接
            {
                adr_sz = sizeof(clnt_adr);
                clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
                event.events = EPOLLIN;
                event.data.fd = clnt_sock;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
                printf("connected client: %d \n", clnt_sock);
            }
            else  //连接之后传递数据
            {
                str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);
                if(str_len == 0)
                {
                    //删除事件
                    epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
                    close(ep_events[i].data.fd);
                    printf("closed client: %d \n", ep_events[i].data.fd);
                }
                else
                {
                    write(ep_events[i].data.fd, buf, str_len);
                }
            }
        }
    }

    close(serv_sock);
    close(epfd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

epoll客户端代码:

#define _GNU_SOURCE
#include "sysutil.h"
#include "buffer.h"
#include <sys/epoll.h>

int main(int argc, char const *argv[])
{
    //创建client套接字
    int sockfd = tcp_client(0);
    //调用非阻塞connect函数
    int ret = nonblocking_connect(sockfd, "localhost", 9981, 5000);
    if(ret == -1)
    {
        perror("Connect Timeout .");
        exit(EXIT_FAILURE);
    }

    //将三个fd设置为Non-Blocking
    activate_nonblock(sockfd);
    activate_nonblock(STDIN_FILENO);
    activate_nonblock(STDOUT_FILENO);

    buffer_t recvbuf; //sockfd -> Buffer -> stdout
    buffer_t sendbuf; //stdin -> Buffer -> sockfd

    //初始化缓冲区
    buffer_init(&recvbuf);
    buffer_init(&sendbuf);

    //创建epoll
    int epollfd = epoll_create1(0);
    if(epollfd == -1)
        ERR_EXIT("create epoll");
    struct epoll_event events[1024];

    uint32_t sockfd_event = 0;
    uint32_t stdin_event = 0;
    uint32_t stdout_event = 0;

    epoll_add_fd(epollfd, sockfd, sockfd_event);
    epoll_add_fd(epollfd, STDIN_FILENO, stdin_event);
    epoll_add_fd(epollfd, STDOUT_FILENO, stdout_event);

    while(1)
    {
        //重新装填epoll事件
        sockfd_event = 0;
        stdin_event = 0;
        stdout_event = 0;
        //epoll无法每次都重新装填,所以给每个fd添加一个空事件

        if(buffer_is_readable(&sendbuf))
        {
            sockfd_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&sendbuf))
        {
            stdin_event |= kReadEvent;
        }
        if(buffer_is_readable(&recvbuf))
        {
            stdout_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&recvbuf))
        {
            sockfd_event |= kReadEvent;
        }
        epoll_mod_fd(epollfd, sockfd, sockfd_event);
        epoll_mod_fd(epollfd, STDIN_FILENO, stdin_event);
        epoll_mod_fd(epollfd, STDOUT_FILENO, stdout_event);

       //监听fd数组
        int nready = epoll_wait(epollfd, events, 1024, 5000);
        if(nready == -1)
            ERR_EXIT("epoll wait");
        else if(nready == 0)
        {
            printf("epoll timeout.\n");
            continue;
        }
        else
        {
            int i;
            for(i = 0; i < nready; ++i)
            {
                int peerfd = events[i].data.fd;
                int revents = events[i].events;
                if(peerfd == sockfd && revents & kReadREvent)
                {
                    //从sockfd接收数据到recvbuf
                    if(buffer_read(&recvbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "server close.\n");
                        exit(EXIT_SUCCESS);
                    }
                }
                if(peerfd == sockfd && revents & kWriteREvent)
                {
                    buffer_write(&sendbuf, peerfd); //将sendbuf中的数据写入sockfd
                }

                if(peerfd == STDIN_FILENO && revents & kReadREvent)
                {
                    //从stdin接收数据写入sendbuf
                    if(buffer_read(&sendbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "exit.\n");
                        exit(EXIT_SUCCESS);
                    }
                }
                if(peerfd == STDOUT_FILENO && revents & kWriteREvent)
                {
                    buffer_write(&recvbuf, peerfd); //将recvbuf中的数据输出至stdout
                }
            }
        }
    }
}

4、条件触发和边缘触发

什么是条件触发和边缘触发?它们是指事件响应的方式,epoll默认是条件触发的方式。条件触发是指:只要输入缓冲中有数据就会一直通知该事件,循环响应epoll_wait。而边缘触发是指:输入缓冲收到数据时仅注册1次该事件,即使输入缓冲中还留有数据,也不会再进行注册,只响应一次。

边缘触发相对条件触发的优点:可以分离接收数据和处理数据的时间点,从实现模型的角度看,边缘触发更有可能带来高性能。

将上面epoll实例改为边缘触发:
1).首先改写 event.events = EPOLLIN | EPOLLET; (EPOLLIN:读取数据事件 EPOLLET:边缘触发方式)
2).边缘触发只响应一次接收数据事件,所以要一次性全部读取输入缓冲中的数据,那么就需要判断什么时候数据读取完了?Linux声明了一个全局的变量:int errno; (error.h中),它能记录发生错误时提供额外的信息。这里就可以用它来判断是否读取完数据

str_len = read(...);
if(str_len < 0)
{
    if(errno == EAGAIN) //读取输入缓冲中的全部数据的标志
        break;
}

3).边缘触发方式下,以阻塞方式工作的read&write有可能会引起服务端的长时间停顿。所以边缘触发一定要采用非阻塞的套接字数据传输形式。那么怎么将套接字的read,write数据传输形式修改为非阻塞模式呢?

//fd套接字文件描述符,将此套接字数据传输模式修改为非阻塞
void setnonblockingmode(int fd)
{
    int flag = fcntl(fd, F_GETFL,0); //得到套接字原来属性
    fcntl(fd, F_SETFL, flag | O_NONBLOCK);//在原有属性基础上设置添加非阻塞模式
}

四、IOCP理解与应用

1、传统服务器的网络IO流程

接到一个客户端连接->创建一个线程负责这个连接的IO操作->持续对新线程进行数据处理->全部数据处理完毕->终止线程。
设计代价可分为四点:
1).每个连接创建一个线程,将导致过多的线程。
2).维护线程所消耗的堆栈内存过大。
3).操作系统创建和销毁线程过大。
4).线程之间切换的上下文代价过大。
这种传统的服务器网络结构称之为会话模型,为防止大量线程的维护,我们可以创建I/O模型。

创建I/O模型要求:
1).允许一个线程在不同时刻给多个客户端进行服务。
2).允许一个客户端在不同时间被多个线程服务。
缺点是会使线程大幅度减少

根据上述则要求一下两点:
1).客户端状态的分离,之前会话模式我们可以通过线程状态得知客户端状态,但现在客户端状态要通过其他方式获取。
2).I/O请求的分离。一个线程不再服务于一个客户端会话,则要求客户端对这个线程提交I/O处理请求。

根据上要求会产生以下模式:
1).会话状态管理模块。它负责接收到一个客户端连接,就创建一个会话状态。
2).当会话状态发生改变,例如断掉连接,接收到网络消息,就发送一个I/O请求给 I/O工作模块进行处理。
3).I/O工作模块接收到一个I/O请求后,从线程池里唤醒一个工作线程,让该工作线程处理这个I/O请求,处理完毕后,该工作线程继续挂起。
则将网络连接 和I/O工作线程分离为三个部分,相互通讯仅依靠 I/O请求。

根据上模式给出一下介意:
1).在进行I/O请求处理的工作线程是被唤醒的工作线程,一个CPU对应一个的话,可以最大化利用CPU。所以 活跃线程的个数 建议等于 硬件CPU个数。
2).工作线程我们开始创建了线程池,免除创建和销毁线程的代价。因为线程是对I/O进行操作的,且一一对应,那么当I/O全部并行时,工作线程必须满足I/O并行操作需求,所以 线程池内最大工作线程个数 建议大于或者等于 I/O并行个数。
3).但是我们可知CPU个数又限制了活跃的线程个数,那么线程池过大意义很低,所以按常规建议 线程池大小 等于 CPU个数*2 左右为佳。例如,8核服务器建议创建16个工作线程的线程池。 上面描述的依然是I/O模型并非IOCP,那么IOCP是什么呢,全称 IO完成端口。
它是一种WIN32的网络I/O模型,既包括了网络连接部分,也负责了部分的I/O操作功能,用于方便我们控制有并发性的网络I/O操作。

WIN32网络I/O模型有如下特点:
1).它是一个WIN32内核对象,所以无法运行于Linux.
2).它自己负责维护了工作线程池,同时也负责了I/O通道的内存池。
3).它自己实现了线程的管理以及I/O请求通知,最小化的做到了线程的上下文切换。
4).它自己实现了线程的优化调度,提高了CPU和内存缓冲的使用率。

2、使用IOCP的基本步骤

1).创建IOCP对象,由它负责管理多个Socket和I/O请求。CreateIoCompletionPort需要将IOCP对象和IOCP句柄绑定。
2).创建一个工作线程池,以便Socket发送I/O请求给IOCP对象后,由这些工作线程进行I/O操作。注意,创建这些线程的时候,将这些线程绑定到IOCP上。
3).创建一个监听的socket。
4).轮询,当接收到了新的连接后,将socket和完成端口进行关联并且投递给IOCP一个I/O请求。注意:将Socket和IOCP进行关联的函数和创建IOCP的函数一样,都是CreateIoCompletionPort,不过注意传参必然是不同的。
5).因为是异步的,我们可以去做其他,等待IOCP将I/O操作完成会回馈我们一个消息,我们再进行处理。
其中需要知道的是:I/O请求被放在一个I/O请求队列里面,对,是队列,LIFO机制。当一个设备处理完I/O请求后,将会将这个完成后的I/O请求丢回IOCP的I/O完成队列。
我们应用程序则需要在GetQueuedCompletionStatus去询问IOCP,该I/O请求是否完成。
其中有一些特殊的事情要说明一下,我们有时有需要人工的去投递一些I/O请求,则需要使用PostQueuedCompletionStatus函数向IOCP投递一个I/O请求到它的请求队列中。

以上就是c++网络编程Linux下的epoll技术和Windows下的IOCP模型的详细内容,更多关于c++网络编程的资料请关注我们其它相关文章!,希望大家以后多多支持我们!

(0)

相关推荐

  • C++ GetDlgItem用法案例详解

    GetDlgItem的用法小结 GetDlgItem用于获得指定控件ID的窗体指针,函数原型如下: HWND GetDlgItem( HWND hDlg, int nIDDlgItem ); CWnd* GetDlgItem(int nID) const; 它的使用说明中有这样一行字,**The returned pointer may be temporary and should not be stored for later use. **,那说明,它返回的指针有可能是有效的,有可能是无效

  • 带你了解C++this指针的用法及其深究

    目录 前言 一.this指针是个什么东东,重要吗? 二.案例理解 主要的用途 总结 前言 今天,码神像一个新车手一样,尝试着用模板来更新一下,不要建议哦,毕竟没有放弃爱情的拓海也不是真正的车神,哈哈,发车了 一.this指针是个什么东东,重要吗? 首先,我以码神的名义起誓,this指针绝对重要,尤其是对于c++这个面向对象编程的语言来说! 有的码手可能要说了:你说重要就重要?那我还说不重要呢? 这个么,空口无凭,我现在来举一个例子: 我们知道对于一个类来说,要有很多工作要做,其中类的成员函数可以

  • C++相交链表和反转链表详解

    目录 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点.如果两个链表没有交点,返回 null . 思路 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表. 双指针思路 递归 总结 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点.如果两个链表没有交点,返回 null . 思路 简单来说,就是求两个链表交点节点的 指针. 这里同学们要注意,交点不是数值相等,而是指针相等. 为了方便举例,假设节点

  • JS调用C++函数抛出异常及捕捉异常详解

    目录 总结 本文讲述如何利用v8::TryCatch捕捉js代码中发生的异常. 首先,声明TryCatch对象. v8::TryCatch trycatch( isolate ); 然后,定义抛出异常的函数: void ThrowException( const v8::FunctionCallbackInfo<v8::Value>& info ) { v8::Isolate* isolate = info.GetIsolate(); v8::HandleScope scope( is

  • 带你粗略了解C++中的深浅拷贝

    目录 一. 背景 二. 代码实现 三. 问题 四. 解决方法 总结 一. 背景 首先看这样一个问题,在Car类中聚合了Engine类 二. 代码实现 下面给出类Car与类Engine的定义 Car.h #ifndef COPY__CAR_H_ #define COPY__CAR_H_ #include "Engine.h" #include <string> using namespace std; class Car { public: // 构造函数 Car(); Ca

  • 带你粗略了解C++流的读写文件

    目录 读写文本文件 二进制读写文件 按指定格式读写文件 总结 读写文本文件 C++的IO流: IO:向设备输入数据和输出数据 设备有: 1)文件 2)控制台 3)特定的数据类型(stringstream) C++中,必须通过特定的已经定义好的类, 来处理IO(输入输出) C++的 IO类库为: 文件流:对文件进行读写操作 头文件: < fstream > ifstream 对文件输入(读文件) ofstream 对文件输出(写文件) fstream 对文件输入或输出 文件的打开方式: 模式标志

  • 一篇文章带你了解C++语法基础--字符串

    目录 总结 字符与整数的关联在于ASCII码:每一个常用字符都对应一个-128 ~ 127 的数字,二者之间是可以进行相互转换的: #include <iostream> using namespace std; int main(){ char wordOne = 'a'; cout << int(wordOne) << endl; int number = 66; cout << char(number) << endl; return 0;

  • C++ namespace案例详解

    在C++语言编写的程序中,变量和函数等的作用范围是有一定限制的.比如,在函数体中定义的一个临时变量就不可以在函数体外使用.为了解决变量和函数等的作用范围,在C++语言中引入了名空间的概念,并增加了关键字namespace和using 在一个名空间中可以定义一组变量和函数,这些变量和函数的作用范围一致,可以将这些变量和函数称为这个名空间的成员. 通过名空间,可以在同一个文件中使用相同的变量名或函数名,只要它们属于不同的名空间.另外,名空间可以使得代码操作具有相同名字但属于不同库的变量.而且,名空间

  • c++网络编程下Linux的epoll技术和Windows下的IOCP模型

    目录 一.IOCP和Epoll之间的异同 1.异 2.同 二:Epoll理解与应用. 1.epoll是什么? 2.epoll与select对比优化 3.epoll是怎么优化select问题的 三.epoll的几个函数的介绍: 1.epoll_create函数 2.epoll_ctl函数 3.epoll_wait函数 4.条件触发和边缘触发 四.IOCP理解与应用 1.传统服务器的网络IO流程 2.使用IOCP的基本步骤 一.IOCP和Epoll之间的异同 1.异 1).IOCP是WINDOWS系

  • Docker Windows下如何安装详细介绍(图文)

    Docker Windows 介绍及安装 前言: 放在三年前,你不认识Docker情有可原,但如果现在你还这么说,不好意思,只能说明你OUT了,行动起来吧骚年,很可能你们公司或者你即将要去的公司,或者你想去的公司很可能就会引入Docker,或者已经引入了Docker. Docker溯源 Docker的前身是名为dotCloud的小公司,主要提供的是基于 PaaS(Platform as a Service,平台及服务)平台为开发者或开发商提供技术服务,并提供的开发工具和技术框架.因为其为初创的公

  • Python入门开发教程  windows下搭建开发环境vscode的步骤详解

    目录 一.环境介绍 二. 搭建python开发环境 2.1 Python版本介绍 2.2 在windows下安装Python环境 2.3 windows下安装VSCode代码编辑器 一.环境介绍 操作系统: win10 64位 python版本: 3.8 IDE: 采用vscode 用到的相关安装包CSDN打包下载地址: http://xiazai.jb51.net/202107/yuanma/Pytho_jb51.rar 二. 搭建python开发环境 2.1 Python版本介绍 因为Pyt

  • WINDOWS下php5.2.4+mysql6.0+apache2.2.4+ZendOptimizer-3.3.0配置

    第一: 装http服务器apache apache.1.jpg (43.95 KB) 2007-9-26 17:36 apache.2.jpg (55.15 KB) 2007-9-26 17:36 apache.3.jpg (64.33 KB) 2007-9-26 17:36 apache.4.jpg (41.78 KB) 2007-9-26 17:36 apache.5.jpg (45.91 KB) 2007-9-26 17:36 apache.6.jpg (28.96 KB) 2007-9-

  • Lua下基本的网络编程示例

    Lua是高度灵活的语言,它往往是在多个平台,包括Web应用程序中使用.成立2004年的Kepler社区提供Lua的Web组件开放源码. 虽然,也有使用Lua已经开发了其他的web框架,我们将主要集中在Kepler社区提供的组件. 应用程序和框架 Orbit 是一个lua的MVC Web框架,它是基于WSAPI. WSAPI是从Lua的Web应用程序抽象的Web主机服务器,是基于许多项目的API. Xavante是一个Lua的Web服务器,提供了一个WSAPI接口. Sputnik是一个wiki/

  • Linux网络编程之基于UDP实现可靠的文件传输示例

    了解网络传输协议的人都知道,采用TCP实现文件传输很简单.相对于TCP,由于UDP是面向无连接.不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题.方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送. 下面展示的是基于linux下C语言实现的一个示例程序,该程序定义一个包的结构体,其中

  • 学习Linux网络编程基本函数

    目录 1,创建套接字socket 函数原型: 参数列表: type的值: protocol得值: 2,绑定套接字bind 函数原型: 参数列表: 3,创建监听:listen 函数原型: 参数列表: 4,等待连接accept 函数原型: 5, 收发消息send和recv 函数原型: 该函数的参数: send的流程: recv的流程: 5,关闭套接字描述符close 函数: 6,基于tcp协议的C/S服务器模型 7,实现代码 服务端: 客户端: 1,创建套接字socket 函数原型: #includ

  • 简析Linux网络编程函数

    目录 1,创建套接字socket 2,绑定套接字bind 3,创建监听:listen 4,等待连接accept 5, 收发消息send和recv 6,关闭套接字描述符close 7,基于tcp协议的C/S服务器模型 8,实现代码 网络编程的一些基本函数:也是实现tcp协议通讯的基本步骤,实现代码在最后,IP需要修改为自己的IP,即可通信: 1,创建套接字socket 函数原型: #include<sys/types.h> #include<sys/socket.h> int soc

  • C/C++ Linux Socket网络编程流程分析

    目录 一.Socket简介 二.Socket编程基础 1. 网络字节序 2. sockaddr数据结构 3. IP地址转换函数 三.Socket编程函数 1. socket函数 2. bind 函数 3. listen 函数 4. accept 函数 5. connect 函数 6. read 函数 7. write 函数 8. close 函数 四.回声服务器案例 1. 服务器 2. 客户端 3. 运行测试 五.总结 之前已经学习了QT的socket编程 和 C/C++在window环境的so

  • QT网络编程UDP下C/S架构广播通信(实例讲解)

    QT有封装好的UDP协议的类,QUdpSocket,里面有我们想要的函数接口.感兴趣的话,可以看看. 先搞服务端吧,写一个子类,继承QDialog类,起名为UdpServer类.头文件要引用我们上边说的QUdpSocket这个类,还有我们想要的布局的类. #ifndef UDPSERVER_H #define UDPSERVER_H #include <QDialog> #include <QLabel> #include <QLineEdit> #include &l

随机推荐