2012-06-16 51 views
16

我对C中的事件驱动编程非常感兴趣,特别是使用套接字,因此我将花一些时间来完成我的研究。C中的事件驱动模型

我们假设我想要构建一个包含大量文件和网络I/O的程序,就像客户机/服务器应用程序一样,基本上第一个问题是这个模型背后的原理是什么。在正常编程中,我会产生新的进程,一个进程怎么能够实际上服务于许多其他的请求。例如,有一些Web服务器可以处理连接,而无需创建线程或其他进程,只需一个主进程。

我知道这很复杂,但它总是很好,至少知道这些编程的基础结构如何。

+0

如果你打算做任何linux套接字的东西,我会建议Beejs指南在这里http://beej.us/guide/bgnet/ – mathematician1975

+0

感谢您的建议,这本书解释或包含任何形式的事件驱动编程参考? – iNDicator

+1

目前还不清楚你在问什么,但你可以先阅读libevent的文档。 – Artefacto

回答

17

您一定要阅读以下内容:http://www.kegel.com/c10k.html。该页面是事件驱动和异步技术的完美概述。

但是,一个快速&肮脏的答案:事件驱动既不是非阻塞,也不是异步。事件驱动意味着进程将监视其文件描述符(和套接字),并且仅当某些事件发生在某些描述符(事件是:收到数据,错误,变为可写,...)时才动作。

BSD套接字具有“select()”功能。被调用时,操作系统将监视描述符,并在其中一个描述符发生某些事件时立即返回进程。

但是,上面的网站有更好的描述(以及有关不同API的详细信息)。

+0

感谢您的链接,似乎这是我正在寻找;) – iNDicator

+0

@iNDicator:你应该接受一个答案! – Frunsi

+0

这个答案**错**。 “事件驱动”是由它的性质异步。什么都不监视。事件本身导出操作。查看我的答案了解更多详情。 – Graham

0

它实际上是非常平台具体如何工作。

如果你的Linux系统这真的不是很难,虽然,你只需要使用“叉”像下面会做的伎俩产卵的过程副本上运行:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet.h> 
#include <signal.h> 
#include <unistd.h> 

int main() 
{ 
    int server_sockfd, client_sockfd; 
    int server_len, client_len; 
    struct sockaddr_in server_address; 
    struct sockaddr_in client_address; 

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0); 

    server_address.sin_family = AF_INET; 
    server_address.sin_addr.s_addr = htonl(INADDR_ANY); 
    server_Address.sin_port = htons(1234); 
    server_len = sizeof(server_address); 
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 

    listen(server_sockfd, 5); 

    signal(SIGCHLD, SIG_IGN); 

    while(1) 
    { 
    char ch; 
    printf("Server Waiting\n"); 
    client_len = sizeof(client_address); 
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len) 

    // Here's where we do the forking, if you've forked already then this will be the child running, if not then your still the parent task. 

    if(fork() == 0) 
    { 
     // Do what ever the child needs to do with the connected client 
     read(client_sockfd, &ch, 1); 
     sleep(5); // just for show :-) 
     ch++; 
     write(client_sockfd, &ch, 1); 
     close(client_sockfd); 
     exit(0); 
    } 
    else 
    { 
     // Parent code here, close and loop for next connection 
     close(client_sockfd); 
    } 
    } 
} 

您可能我不得不小心操作那些代码,现在我还没有在Linux机器附近进行测试编译,而且我几乎从内存中输入了它。

但是使用fork是在基于Linux/Unix的系统下在C中执行此操作的标准方式。在Windows下这是一个非常不同的故事,我不能完全记住所有需要的代码(我习惯于在C#中编写代码的方式),但设置套接字几乎是一样的您需要使用'Winsock'API以获得更好的兼容性。

你可以(我认为是这样)仍然使用标准Berkeley套接字在Windows下,但它充满陷阱和孔,适用于Windows的Winsock这是一个良好的开端:

http://tangentsoft.net/wskfaq/

至于我我也知道,如果你使用Winsock,它有一些东西来帮助产生和多客户端,但是我个人通常只是分离出一个单独的线程,然后复制套接字连接,然后返回到收听我的服务器。

+1

非常感谢你的例子,但是这里没有创建一个新的子进程?所以如果我们有30个客户端连接,我们不会得到30个新的进程? – iNDicator

+1

事实上,它确实创建了子进程,但是没有完整的进程,部分任务从父进程中剥离出来,只保留了服务的生命周期。另一种方法是使用Linux PThreads,它仍然会产生一个子进程,而不是像叉子那样在它自己的内存池中。绝大多数Linux服务使用Fork的方式。 – shawty

+0

嗯,这是我想要防止,看到Frunsi的职位。这似乎是我需要的!不是你的没用;) – iNDicator

1

事件驱动的编程基于事件循环。循环只是等待一个新的事件,调度处理事件的代码,然后循环等待下一个事件。在套接字的情况下,你在谈论“异步网络编程”。这涉及select()或其他一些选项,如Kqueue()等待事件循环中的事件。需要将套接字设置为非阻塞,以便在读取()或写入()时,代码不会等待I/O完成。

异步网络编程可能非常复杂,而且很难得到正确的结果。查看几个介绍herehere。我强烈建议使用库如libeventliboop来获得此权利。

+0

感谢您的链接 – iNDicator

1

这种TCP服务器/客户端可以通过使用select(2)调用和非阻塞套接字来实现。

使用非阻塞套接字比阻塞套接字更为棘手。

实施例:

connect调用通常返回-1立即和设置errno EINPROGRESS非阻塞插座一起使用时。在这种情况下,您应该使用select等待连接打开或失败。 connect也可能返回0.如果您创建到本地主机的连接,则可能发生这种情况。 这样你可以服务其他套接字,而一个套接字打开TCP连接。

2

“这是什么模式背后的理念是”

事件驱动方式有没有“监控”,但事件本身发起行动。

通常这是由中断引发的,该中断是从外部设备向系统发出的信号,或者(在软件中断的情况下)异步过程。

https://en.wikipedia.org/wiki/Interrupt

延伸阅读似乎是在这里:

https://docs.oracle.com/cd/E19455-01/806-1017/6jab5di2m/index.html#sockets-40 - “中断驱动套接字I/O”

而且http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html有中断驱动套接字的一些例子,以及其他套接字编程示例。