突然发现Linux下socket的怪异之处!

Lamb0145 2012-08-06 08:00:28
有两点奇怪的地方。

第一、阻塞模式下,无accept,connect返回成功

背景:connect运行在阻塞模式下,服务端也是阻塞的,只能接收一个客服端的请求。

问题:服务端运行在死循环里,当接收到一个客户端连接后,accept并进入交互。此时再有一个客户端连接上来,connect返回成功,发送消息被缓存起来,等待第一个客户端退出后。服务端accept与此客户端建立连接。

问题是都没accept,应该connect不成功才对,原因是啥?如果是listen放到缓存队列里的话,那怎么样可以不缓存,让connect直接返回失败!

第二、非阻塞模式下,connect均返回失败

非阻塞模式下,本地客户端连接本地服务端,服务端accept成功,但客户端connect返回失败。远程客户端连接本地服务端,连不上。。。是何原因。代码整理之后贴出来
...全文
224 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
mymtom 2012-08-13
  • 打赏
  • 举报
回复
第一、阻塞模式下,无accept,connect返回成功
====
这个很正常,listen后,系统内部会接受连接,connect就会成功。但是如果服务器一直不accept,然后多客户端不停的connect,最终会失败。

第二、非阻塞模式下,connect均返回失败
====
非阻塞模式下,connect失败是很正常的,在connect不能立即完成时,就会返回错误,但是连接的过程会继续,这时需要用select来检查连接是否完成(成功或失败)
http://developerweb.net/viewtopic.php?id=3196
[EINPROGRESS]
O_NONBLOCK is set for the file descriptor for the socket and the connection cannot be immediately established; the connection will be established asynchronously.
wangweizhaoxin 2012-08-10
  • 打赏
  • 举报
回复
1.对于listen第二个参数的问题,有两点需要理解一下:1.第二个参数的理解:对于listen的第二个参数参数从未有过正是的定义。一种说法是两个队列的总和的最大值;另一种说法是已完成连接的队列的长度,再乘以一个模糊因子就是总的长度,模糊因子为1.5,不管哪种说法,只是让我们理解的一种手段。2.当listen队列已满的时候,当客户端再发送一个SYN,TCp就会忽略此分节,并且不发送RST。客户TCP将重发SYN,期望不久就能在队列中找到空闲条目。

那么你的listen设置为1,并且多个客户端连接依然没有问题就好理解了。
Lamb0145 2012-08-10
  • 打赏
  • 举报
回复
我回去试了下用select并判断fd是否可写的方式来获取连接,但依然解决不了多客户端连接问题。选择非阻塞后再换回阻塞模式跟原本的阻塞模式无两样。

如果一直使用非阻塞模式,当客户端向服务器send的时候,如果服务端还没有accept,则会返回出错。我对这个问题大体上也已经明确了。虽然没有在这方面把问题解决了。
Lamb0145 2012-08-09
  • 打赏
  • 举报
回复
嗯,wangweizhaoxin你说的对,这我之前也已经发现了,关键是以前对三次握手还不明确。还有就是listen的第二个参数不管设置几都没啥用,设1,多客户端连接依然没问题,不知道是不是我没用对。


对于问题2:我去试试你的建议,你的回答挺明确的,就是有点太言简意赅了吧。
wqkjj 2012-08-07
  • 打赏
  • 举报
回复
通常服务器端收到请求后,会启动一个线程或者fork一个进程来处理客户端的请求,而服务器的主线程或者进程会继续侦听和接受下一个客户端的联接请求,如果存在联接请求,则再次启动线程或者fork子进程,如果循环。
Lamb0145 2012-08-07
  • 打赏
  • 举报
回复
虽然我规避了这个问题。解决了问题,但是我想知道怎么才能正面解决,或者知道出现问题的原因。
wangweizhaoxin 2012-08-07
  • 打赏
  • 举报
回复
问题1:判断一个连接的成功是三次握手,三次握手成功后才进行accept。在listen的时候其实有两个队列,一个是已经完成三次握手的队列,就是连接成功,还有一个是等待三次握手的队列。所有connect成功跟有无accept无关。
问题2:当connect在非阻塞的模式下时,并没有等待三次握手的成功,所有返回失败。如果想用connect的非阻塞模式,可以配合select使用。
Lamb0145 2012-08-07
  • 打赏
  • 举报
回复
楼上你的说法是用多线程或多进程实现的支持多用户的服务端网络编程,这个我知道。

关键我现在要实现的是只能支持一个用户的服务端和客户端。其他用户连上来应该直接返回错误,不应该连接成功。

现在我大概知道了错误出现的原因是在三次握手中服务端和客户端认为的连接建立的时机不同造成的。但想看看有没有更多人对此有不同的看法,特别针对第二个问题。

Lamb0145 2012-08-06
  • 打赏
  • 举报
回复
服务端部分代码:
int server_fd;
int reuse_addr = 1;
struct sockaddr_in serveraddr;

/* create socket */
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == server_fd)
{
PROXY_LOG_ERR("Create adm socket error as a server!\n");
return -1;
}

/* set socket port reusable */
if (0 > setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(int)))
{
PROXY_LOG_ERR("Set adm sockopt failed!\n");
return -1;
}

/* bind socket */
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(ip);
if (-1 == bind(server_fd, (struct sockaddr *)(&serveraddr), sizeof(serveraddr)))
{
PROXY_LOG_ERR("Bind adm socket error!\n");
return -1;
}

/* listen socket */
if (-1 == listen(server_fd, 20))
{
PROXY_LOG_ERR("Listen adm socket error!\n");
return -1;
}

int client_fd;
socklen_t size;
struct sockaddr_in clientaddr;

size = sizeof(clientaddr);
client_fd = accept(fd, (struct sockaddr *)(&clientaddr), &size);
if (-1 == client_fd)
{
PROXY_LOG_ERR("Accept adm socket error!\n");
return -1;
}

客户端:
int server_fd;
struct sockaddr_in serveraddr;
struct timeval time_out = {C_TIME_OUT, 0};
socklen_t len = sizeof(time_out);

/* create socket */
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == server_fd)
{
perror("socket");
return -1;
}

setsockopt(server_fd, SOL_SOCKET, SO_SNDTIMEO, &time_out, len);

/* bind socket */
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(ip);
if (-1 == connect(server_fd, (struct sockaddr *)(&serveraddr), sizeof(struct sockaddr)))
{
if (errno == EINPROGRESS) /* timeout in connect */
{
printf("Time out!\n");
return -1;
}
perror("connect");
return -1;
}

23,118

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧