24

我已经看到许多关于send()讨论底层协议的问题。我完全清楚,对于 TCP,任何消息都可能在发送时被分解成多个部分,并且不能保证接收者会在一次原子操作中获得消息。在这个问题中,我只讨论send()系统调用与本地系统的网络层交互时的行为。

根据 POSIX 标准和send()我阅读的文档,要发送的消息的长度由length参数指定。请注意:send()发送一条消息,长度为length。更远:

如果发送套接字上没有可用空间来保存要传输的消息,并且套接字文件描述符没有 O_NONBLOCK设置,send()则应阻塞直到空间可用。如果发送套接字没有空间来保存要传输的消息,并且套接字文件描述符确实已O_NONBLOCK设置,则将 send()失败。

我认为在这个定义中 send() 没有任何可能返回除-1(这意味着没有数据在内核中排队等待传输)或length之外的任何值,这意味着整个消息在内核中排队等待传送。即,在我看来,在本地排队消息以在内核中传递方面send()必须是原子的。

  1. 如果内核中的套接字队列中有足够的空间容纳整个消息并且没有信号发生(正常情况),则将其复制并返回length
  2. 如果在此期间出现信号send(),则它必须返回-1。显然,在这种情况下,我们不能将消息的一部分排队,因为我们不知道发送了多少。所以在这种情况下什么都不能发送。
  3. 如果内核中的套接字队列中没有足够的空间容纳整个消息并且套接字正在阻塞,那么根据上面的语句send()必须阻塞直到空间变得可用。然后消息将被排队并send()返回长度
  4. 如果内核中的套接字队列中没有足够的空间容纳整个消息并且套接字是非阻塞的,则send()必须失败(返回-1)并将errno设置为EAGAINor EWOULDBLOCK。同样,由于我们返回-1,很明显在这种情况下,消息的任何部分都不能排队。

我错过了什么吗?send() 是否有可能返回一个值>=0 && <length?在什么情况下?非 POSIX/UNIX 系统呢?Windowssend()实现是否符合此要求?


在 Asp.net Identity MVC 5 中创建角色

关于使用新的 Asp.net 身份安全框架的文档很少。

我已经拼凑了我可以尝试创建一个新角色并向其添加用户的内容。我尝试了以下方法:在 ASP.NET 身份中添加角色

看起来它可能已经从这个博客中获得了信息:使用 asp.net 身份构建一个简单的待办事项应用程序并将用户与待办事项相关联

我已将代码添加到模型更改时运行的数据库初始化程序。它在功能上失败并RoleExists出现以下错误:

System.InvalidOperationException发生在 mscorlib.dll 中的实体类型 IdentityRole 不是当前上下文模型的一部分。

protected override void Seed (MyContext context)
{
    var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)); 
    var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));

    // Create Admin Role
    string roleName = "Admins";
    IdentityResult roleResult;

    // Check to see if Role Exists, if not create it
    if (!RoleManager.RoleExists(roleName))
    {
        roleResult = RoleManager.Create(new IdentityRole(roleName));
    }
}

任何帮助表示赞赏。

4

6 回答 6

12

您的第 2 点过于简单化了。send返回大于零但小于长度的值的正常情况(请注意,正如其他人所说,它永远不会返回零,除非长度参数可能为零)是当消息足够长以导致阻塞时,并且在某些内容已经发送后,一个中断信号到达。在这种情况下,send不能失败EINTR(因为这会阻止应用程序知道它已经成功发送了一些数据)并且它不能重新阻塞(因为信号正在中断,而关键是要摆脱阻塞) ,因此它必须返回已发送的字节数,该字节数小于请求的总长度。

于 2013-10-31T05:09:56.273 回答
6
  1. 根据 Posix 规范和我 30 年来见过的所有man 2 发送send()页面,是的,可以返回任何值 > 0 和 <= length。请注意,它不能返回零。

  2. 根据几年前关于所有 TCP 实现者所在的 news:comp.protocols.tcp-ip 的讨论,在send()将所有数据传输到套接字发送缓冲区之前,阻塞实际上不会返回:换句话说,返回值要么是 -1 要么length.是所有已知的实现都是如此,对于write(), writev(), sendmsg(), writev(),也是如此

于 2013-10-31T02:21:21.460 回答
3

我知道这个东西在 Linux 上是如何工作的,使用 GNU C 库。在这种情况下,您问题的第 4 点会有所不同。如果您O_NONBLOCK为文件描述符设置了标志,并且如果无法在内核中原子地对整个消息进行排队,则send()返回实际发送的字节数(可以在 1 和长度之间),并errno设置为EWOULDBLOCK.

(在阻塞模式下工作的文件描述符send()会阻塞。)

于 2014-04-02T03:03:40.920 回答
2

有可能send()返回一个value >= 0 && < length. 如果发送缓冲区的空间小于调用send(). 类似地,如果发送者知道的当前接收窗口大小小于消息的长度,则可能只发送部分消息。有趣的是,我在 Linux 上通过 localhost 连接看到了这种情况,当时接收进程从接收缓冲区卸载它正在接收的数据很慢。

我的感觉是,一个人的实际经验会因实施而有所不同。从这个 Microsoft 链接中可以清楚地看出,可能会出现小于长度的非错误返回值。

如果发送长度为零的消息,也可以获得零返回值(同样,至少在某些实现中)。

该答案基于我的经验,并特别借鉴了此 SO 答案

编辑:从这个答案及其评论来看,显然EINTR只有在发送任何数据之前发生中断时才会导致失败,这将是获得这种返回值的另一种可能方式。

于 2013-10-31T02:47:44.707 回答
2

在 64 位 Linux 系统上:

sendto(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4294967296, 0, NULL, 0) = 2147479552

因此,即使尝试发送低 4GB 的数据,Linux 也会退缩并发送不到 2GB。所以,如果你认为你会要求它发送 1TB 并且它会耐心地坐在那里,请继续希望。

同样,在只有几 KB 空闲的嵌入式系统上,不要认为它会失败或等待某些东西——它会尽可能多地发送,并告诉你有多少,让你重试其余的(或同时做其他事情)。

每个人都同意,在 EINTR 的情况下,可以有一个短发送。但是 EINTR 可以随时发生,所以总是有一个短发送。

最后,POSIX 说返回发送的字节数,句号。整个 Unix 和将其形式化的 POSIX 建立在短读/写的概念之上,这允许 POSIX 系统的实现从最小的嵌入式扩展到具有众所周知的“大数据”的超级计算机。因此,无需尝试阅读字里行间,并为您手头的特定临时实现找到放纵。那里有更多的实现,只要你遵循标准的话,你的应用程序就可以在它们之间移植。

于 2019-02-06T12:15:09.617 回答
0

澄清一点,它说:

将阻塞直到空间可用。

有几种方法可以从该块/睡眠中唤醒:

  • 有足够的空间可用。
  • 一个信号中断当前的阻塞操作。
  • SO_SNDTIMEO为套接字设置并且超时到期。
  • 其他,例如套接字在另一个线程中关闭。

所以事情就这样结束了:

  1. 如果内核中的套接字队列中有足够的空间容纳整个消息并且没有信号发生(正常情况),则它被复制并返回长度。
  2. 如果在 send() 期间出现信号,则它必须返回 -1。显然,在这种情况下,我们不能将消息的一部分排队,因为我们不知道发送了多少。所以在这种情况下什么都不能发送。
  3. 如果内核中的套接字队列中没有足够的空间容纳整个消息并且套接字正在阻塞,那么根据上面的语句 send() 必须阻塞直到空间可用。然后消息将被排队并且 send() 返回长度。 然后send()可以被信号中断,发送超时可以过去,......导致短发送/部分写入。如果没有将任何内容复制到发送缓冲区,则合理的实现将返回 -1 并设置errno为适当的值。
  4. 如果内核中的套接字队列中没有足够的空间容纳整个消息并且套接字是非阻塞的,那么 send() 必须失败(返回 -1)并且 errno 将被设置为 EAGAIN 或 EWOULDBLOCK。同样,由于我们返回 -1,很明显在这种情况下,消息的任何部分都不能排队。
于 2014-02-24T13:22:44.427 回答