我们假设有一个客户端与服务器建立了很多短生命的连接.
如果客户端关闭连接,则客户端上将有许多TIME_WAIT状态的端口.由于客户端耗尽本地端口,因此无法快速进行新的连接尝试.
如果服务器关闭了连接,我会TIME_WAIT在服务器端看到很多s.但是,这有什么害处吗?客户端(或其他客户端)可以继续进行连接尝试,因为它永远不会耗尽本地端口,并且TIME_WAIT服务器端的状态数将增加.最终会发生什么?有什么不好的事吗?(减速,崩溃,掉线等)
请注意,我的问题不是"目的是TIME_WAIT什么?" 但是"如果TIME_WAIT服务器上有这么多状态,会发生什么?" 我已经知道在TCP/IP中关闭连接时会发生什么以及为什么TIME_WAIT需要状态.我不是想解决它,但只是想知道它的潜在问题.
简单来说,让我们说netstat -nat | grep :8080 | grep TIME_WAIT | wc -l打印100000.会发生什么?O/S网络堆栈是否会变慢?"打开文件太多"错误?或者,没什么好担心的?
我在覆盆子pi上运行自己的http服务器.问题是当我停止程序并重新启动它时,端口不再可用.有时我在收到大量请求时会遇到同样的问题.
我想使用SO_REUSEADDR,以便即使出现错误但我也可以继续使用该端口但是没有运气设置它.以下是我的代码.
我得到的错误是"绑定时出错:地址已在使用中".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
printf("Starting Listener\n");
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET; …Run Code Online (Sandbox Code Playgroud) 如何在Http消息中处理服务器错误?
假设我已经发送了消息的标题,并且我正在流式传输消息正文,当遇到意外错误时该怎么办.
我也假设这个错误是在生成内容而不是连接错误时引起的.
(很棒)简化代码:
// I can define any transfer encoding or header fields i need to.
send(header); // Sends the header to the Http client.
// Using an iterable instead of stream for code simplicity's sake.
Iterable<String> stream = getBodyStream();
Iterator<String> iterator = stream.iterator();
while (iterator.hasNext()) {
String string;
try {
string = iterator.next();
catch (Throwable error) { // Oops! an error generating the content.
// What do i do here? (In regards to the Http protocol)
}
send(string); …Run Code Online (Sandbox Code Playgroud) 因此,启动终止的对等体 - 即首先调用close() - 将最终处于TIME_WAIT状态.[...]
但是,在服务器上处于TIME_WAIT状态的许多套接字可能会出现问题,因为它最终可能会阻止接受新连接.[...]
相反,请设计应用程序协议,以便始终从客户端启动连接终止.如果客户端总是知道它何时读取了所有剩余数据,则它可以启动终止序列.例如,浏览器在读取所有数据时可以从Content-Length HTTP标头中获知,并且可以启动关闭.(我知道在HTTP 1.1中它会保持打开一段时间以便重用,然后关闭它.)
我想使用TcpClient/TcpListener来实现它,但目前尚不清楚如何使其正常工作.
这是大多数MSDN示例所示的典型方式 - 双方调用Close(),而不仅仅是客户端:
private static void AcceptLoop()
{
listener.BeginAcceptTcpClient(ar =>
{
var tcpClient = listener.EndAcceptTcpClient(ar);
ThreadPool.QueueUserWorkItem(delegate
{
var stream = tcpClient.GetStream();
ReadSomeData(stream);
WriteSomeData(stream);
tcpClient.Close(); <---- note
});
AcceptLoop();
}, null);
}
private static void ExecuteClient()
{
using (var client = new TcpClient())
{
client.Connect("localhost", 8012);
using (var stream = client.GetStream())
{
WriteSomeData(stream);
ReadSomeData(stream);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我运行20个客户端之后,套装软件显示了很多插座的客户端和服务器都 …
我在我的应用程序中出现了一个似乎不可重现的故障.我有一个TCP套接字连接失败,应用程序尝试重新连接它.在第二次调用connect()尝试重新连接时,我得到了errno == EADDRNOTAVAIL的错误结果,connect()的手册页表示:"指定的地址不能从本地计算机上获得."
看看对connect()的调用,第二个参数似乎是错误引用的地址,但据我所知,这个参数是远程主机的TCP套接字地址,所以我对这个人感到困惑页面引用本地计算机.是否我的本地计算机无法访问远程TCP套接字主机的此地址?如果是这样,为什么会这样呢?它必须在连接失败之前第一次成功调用connect()并尝试重新连接并获得此错误.connect()的参数两次都是相同的.
这个错误会不会是一个短暂的错误,如果我等了很长时间,如果我再次尝试调用connect可能已经消失了?如果没有,我应该如何从这次失败中恢复?
我知道TIME_WAIT是TCP/IP的一个组成部分,但是在SO(和其他地方)有很多问题,每秒创建多个套接字,服务器最终耗尽短暂的端口.
我发现的是,当使用TCPClient(或者Socket就此而言)时,如果我调用了Close()或者Dispose()方法,则套接字的TCP状态变为TIME_WAIT并且将在完全关闭之前考虑超时时间.
但是,如果只是将变量设置null为套接字将在下一次GC运行时完全关闭,这当然可以强制执行,而不会经历TIME_WAIT状态.
这对我来说没有多大意义,因为这是一个IDisposable对象不应该GC也调用Dispose()对象的方法?
这是一些PowerShell代码,演示了(在这台机器上没有安装VS).我使用Sysinternals的TCPView实时检查套接字状态:
$sockets = @()
0..100 | % {
$sockets += New-Object System.Net.Sockets.TcpClient
$sockets[$_].Connect('localhost', 80)
}
Start-Sleep -Seconds 10
$sockets = $null
[GC]::Collect()
Run Code Online (Sandbox Code Playgroud)
使用此方法,套接字永远不会进入TIME_WAIT状态.同样的,如果我只是手动调用之前关闭该应用程序Close()或Dispose()
有人可以解释一下是否这是一个好习惯(我想人们会说这不是).
编辑
GC已经回答了这个问题,但我仍然有兴趣找出为什么这会对套接字状态产生任何影响,因为这应该由操作系统控制,而不是.NET.
还有兴趣了解使用此方法来防止TIME_WAIT状态并最终是否是某个地方的错误(即,所有套接字是否应该通过TIME_WAIT状态?)是否是一个好习惯?
我有一个问题,MongoDB无法响应并拒绝生产中的连接.
在测试中,我能够通过向我的应用程序发送大量请求来一致地重现"锁定".
以下是mongostat的示例输出:
0 0 0 0 0 1 0 1.3g 1.8g 177m 0 0 0 0|0 0|0 62b 1k 18 19:40:11
0 0 0 0 0 1 0 1.3g 1.8g 177m 0 0 0 0|0 0|0 62b 1k 18 19:40:12
--------------LOAD TEST BEGINS-----------------------
1 56 0 0 1 58 0 1.3g 1.81g 177m 0 0 0 0|0 0|0 10k 302k 18 19:40:13
10 116 0 0 2 127 0 1.3g 1.81g 177m 0 0 0 0|0 1|0 32k 263k …Run Code Online (Sandbox Code Playgroud) 以下是我的测试夹具的精髓 -
SetUp()
{
g_listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/* localhost is the server */
bind(g_listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(g_listen_sock, max_connections);
}
testcase()
{
hdl = accept(g_listen_sock, NULL, NULL);
-- send()/recv() data on the socket --
}
TearDown()
{
shutdown(g_listen_sock, SHUT_RDWR);
close(g_listen_sock);
g_listen_sock = INVALID_SOCKET;
}
Run Code Online (Sandbox Code Playgroud)
在应用程序的正常使用中,侦听套接字仅在应用程序的生命周期中绑定一次,但是测试设置会反复打开和关闭侦听套接字.测试用例的第一次迭代工作正常但后续迭代在使用errno == 98ie EADDRINUSE 的bind()调用时失败.
我该如何避免这种情况?理想情况下,该解决方案不需要我具有单独的代码测试版本,例如在测试时使用SO_REUSEADDR.
PS - 相同的代码在Windows上正常工作,在Linux上发生bind()失败.
我开发了一个包含一个小型http服务器的应用程序.
我的应用程序是在启动时启动的.如果应用程序正常停止(etc/init.d/myappli stop),套接字将被关闭
close (socket_desc);
Run Code Online (Sandbox Code Playgroud)
但如果我用kill -9套接字杀死它将不会被关闭
http服务器代码:
void http_server_init(void)
{
struct sockaddr_in server;
int cr_port;
for(;;) {
cr_port = conf.port;
int i = (DEFAULT_PORT == cr_port)? 1 : 0;
//Create socket
cr_socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (cr_socket_desc == -1)
{
LOG (ERROR,"Could not open server socket, Error no is : %d, Error description is : %s", errno, strerror(errno));
sleep(1);
continue;
}
/* enable SO_REUSEADDR */
int reusaddr = 1;
if (setsockopt(cr_socket_desc, SOL_SOCKET, SO_REUSEADDR, &reusaddr, …Run Code Online (Sandbox Code Playgroud) 我已阅读何时需要 TCP 选项 SO_LINGER (0)?以及其他几个相关的问题和答案,但我无法重现SO_LINGER这些帖子中解释的任何行为。我将在这里分享我的众多实验之一。
我正在以下环境中执行此实验。
$ lsb_release -d
Description: Debian GNU/Linux 9.0 (stretch)
$ gcc -dumpversion
6.3.0
Run Code Online (Sandbox Code Playgroud)
这是一个行为不端的客户端连接到服务器,但在 90 秒内未收到任何数据的示例。
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main()
{
int sockfd;
int ret;
struct addrinfo hints, *ai;
char buffer[256];
ssize_t bytes;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((ret = getaddrinfo(NULL, "8000", &hints, &ai)) == -1) {
fprintf(stderr, "client: getaddrinfo: %s\n", gai_strerror(ret)); …Run Code Online (Sandbox Code Playgroud) sockets ×8
c ×4
tcp ×3
.net ×2
linux ×2
c# ×1
c++ ×1
http ×1
ip-address ×1
mongodb ×1
networking ×1
powershell ×1
protocols ×1
raspberry-pi ×1
so-linger ×1
tcpclient ×1
tcplistener ×1
tcpsocket ×1
time-wait ×1