Linux 带有套接字的 C 语言中的事件驱动模型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11062674/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Event-driven Model in C with Sockets
提问by iNDicator
I am really interested in event-driven programming in C especially with sockets so I am going to dedicate some time doing my researches.
我对 C 中的事件驱动编程特别感兴趣,尤其是套接字,所以我将花一些时间进行研究。
Let's assume that I want to build a program with much File and Network I/O like a client/server app, basically, the first question is what is the philosophy behind this model. While in normal programming I would spawn new processes, how come a single process can actually serve many other requests. For example, there are some web-servers which can handle connections without creating threads or other processes, just one main process.
假设我想构建一个像客户端/服务器应用程序一样具有大量文件和网络 I/O 的程序,基本上,第一个问题是这个模型背后的哲学是什么。虽然在正常编程中我会产生新的进程,但为什么单个进程实际上可以服务许多其他请求。例如,有一些 web 服务器可以处理连接,而无需创建线程或其他进程,只需一个主进程。
I know this is complicated but it's always nice to know how different solutions work.
我知道这很复杂,但知道不同的解决方案如何工作总是很好。
采纳答案by Frunsi
You definitely must read the following: http://www.kegel.com/c10k.html. That page is the perfect overview of event-driven and asynchronous techniques.
您绝对必须阅读以下内容:http: //www.kegel.com/c10k.html。该页面是事件驱动和异步技术的完美概述。
However, a quick & dirty answer: event-driven is neither non-blocking, nor asynchronous.
然而,一个快速而肮脏的答案:事件驱动既不是非阻塞的,也不是异步的。
Event-driven means, that the process will monitor its file descriptors (and sockets), and act only when some event occurs on some descriptor (events are: data received, error, became writeable, ...).
事件驱动意味着进程将监视其文件描述符(和套接字),并且仅在某些描述符上发生某些事件时才采取行动(事件是:数据接收、错误、变为可写……)。
BSD sockets have the "select()" function. When called, the OS will monitor the descriptors, and return to the process as soon as some event on one of the descriptors occurs.
BSD 套接字具有“select()”功能。调用时,操作系统将监视描述符,并在其中一个描述符上发生某些事件时立即返回进程。
However, the website above has much better descriptions (and details about the different APIs).
但是,上面的网站有更好的描述(以及有关不同 API 的详细信息)。
回答by shawty
It's actually very platform specific as to how that works.
它实际上是非常特定于平台的,它是如何工作的。
If your running on a linux system it's really not to difficult though, you simply need to spawn a copy of your process using 'fork' something like the following would do the trick:
如果您在 linux 系统上运行它真的不难,您只需要使用“fork”生成您的进程的副本,如下所示:
#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);
}
}
}
You may have to fiddle with that code a little I'm not near a Linux box at the moment to do a test compile, and I've pretty much typed it from memory.
您可能需要稍微处理一下该代码,我现在不在 Linux 机器附近进行测试编译,而且我几乎是从内存中键入的。
Using fork however is the standard way to do this in C under a Linux / Unix based system.
然而,在基于 Linux / Unix 的系统下,使用 fork 是在 C 中执行此操作的标准方法。
Under windows it's a very different story, and one which I can't quite remember all the code needed (I'm way to used to coding in C# these days) but setting up the socket is pretty much the same except you need to use the 'Winsock' API for better compatibility.
在 Windows 下,这是一个非常不同的故事,我不太记得需要的所有代码(这些天我习惯用 C# 编码)但是设置套接字几乎相同,只是你需要使用'Winsock' API 以获得更好的兼容性。
You can (I believe anyway) still use standard berkley sockets under windows but it's full of pitfalls and holes, for windows winsock this is a good place to start:
您可以(无论如何我相信)仍然可以在 windows 下使用标准的 Berkley 套接字,但它充满了陷阱和漏洞,对于 windows winsock,这是一个很好的起点:
http://tangentsoft.net/wskfaq/
http://tangentsoft.net/wskfaq/
As far as I'm aware too, if your using Winsock it has stuff in to help with the spawning and multi client, myself personally however, I usually just spin off a separate thread and copy the socket connection to that, then go back into the loop listening to my server.
据我所知,如果您使用 Winsock,它有帮助生成和多客户端的东西,但是我个人通常只是分离一个单独的线程并将套接字连接复制到该线程,然后返回循环监听我的服务器。
回答by Matt
Event driven programming is based on an event loop. The loop simply waits for a new event, dispatches code to handle the event, then loops back to wait for the next event. In the case of sockets, you're talking about "asynchronous network programming". This involves select() or some other option like Kqueue() to wait for the events in the event loop. Sockets would need to be set to non blocking, so that when you read() or write() your code won't wait for the I/O to complete.
事件驱动编程基于事件循环。循环只是等待一个新事件,调度代码来处理该事件,然后循环返回以等待下一个事件。在套接字的情况下,您正在谈论“异步网络编程”。这涉及select() 或其他一些选项,如Kqueue() 以等待事件循环中的事件。套接字需要设置为非阻塞,这样当你 read() 或 write() 你的代码不会等待 I/O 完成。
Asynchronous network programming can be very complex, and tricky to get right. Check out a couple of introductions hereand here. I strongly suggest using a library such as libeventor liboopto get this right.
异步网络编程可能非常复杂,而且很难做到正确。在此处和此处查看一些介绍。我强烈建议使用诸如libevent或liboop 之类的库来做到这一点。
回答by SKi
That kind of TCP servers/clients can be implemented by using select(2)
call and non-blocking sockets.
这种 TCP 服务器/客户端可以通过使用select(2)
调用和非阻塞套接字来实现。
It is more tricky to use non-blocking sockets than blocking sockets.
使用非阻塞套接字比使用阻塞套接字更棘手。
Example:
例子:
connect
call usually return -1 immediately and set errno EINPROGRESS
when non-blocking socket are used. In this case you should use select
to wait when connection is opened or failed. connect
may also return 0. This can happen if you create connection to the local host.
This way you can serve other sockets, while one socket is opening a TCP connection.
connect
call 通常立即返回 -1 并EINPROGRESS
在使用非阻塞套接字时设置 errno 。在这种情况下,您应该select
在连接打开或失败时使用等待。connect
也可能返回 0。如果您创建到本地主机的连接,就会发生这种情况。这样您就可以为其他套接字提供服务,而一个套接字正在打开 TCP 连接。
回答by Graham
"what is the philosophy behind this model"
“这个模型背后的哲学是什么”
Event driven means there is no "monitoring", but that the event itself initiates the action.
事件驱动意味着没有“监控”,而是事件本身启动动作。
Usually this is initiated by an interrupt, which is a signal to the system from an external device, or (in the case of a software interrupt) an asynchronous process.
通常这是由中断启动的,中断是从外部设备向系统发出的信号,或者(在软件中断的情况下)异步过程。
https://en.wikipedia.org/wiki/Interrupt
https://en.wikipedia.org/wiki/Interrupt
Further reading seems to be here:
进一步阅读似乎在这里:
https://docs.oracle.com/cd/E19455-01/806-1017/6jab5di2m/index.html#sockets-40- "Interrupt-Driven Socket I/O"
https://docs.oracle.com/cd/E19455-01/806-1017/6jab5di2m/index.html#sockets-40- “中断驱动的套接字 I/O”
Also http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.htmlhas some examples of Interrupt-Driven Sockets, as well as other socket programming examples.
另外http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html有一些中断驱动的套接字示例,以及其他套接字编程示例。