地址已在使用中
Address Already in Use.
最近我一直在编写一些客户端代码,用于使用线程从服务器发送和接收消息。以下代码在运行时表现异常。在输入要发送到服务器的消息后,代码完成了任务,尽管出现了"套接字已在使用"错误,但服务器会收到它。但我尝试发送到服务器上的每一条后续消息都没有立即收到,但当客户端程序终止时,似乎都会同时收到。
(此外,我确信错误发生在客户端,如果对输出函数进行注释,则不会显示出奇怪的行为。)
如何修复此错误?
客户端
#include <stdio.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
#include <errno.h>
#include <pthread.h>
void* input(void* ptr)
{
int on = 1;
bool *input_done = ((struct thread_args*)ptr)->process_done;
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
bind(sock,res->ai_addr,res->ai_addrlen);
connect(sock,res->ai_addr,res->ai_addrlen);
cin.getline(msg,256);
if (msg[0] == '/') {exit(1);}
send(sock,msg,sizeof msg,0);
cout << "You:" << msg << endl;
*input_done = 1;
close(sock);
pthread_exit(NULL);
}
void* output(void* ptr)
{
int on = 1;
bool *output_done = ((struct thread_args*)ptr)->process_done;
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
bind(sock,res->ai_addr,res->ai_addrlen);
connect(sock,res->ai_addr,res->ai_addrlen);
recv(sock,msg,sizeof msg,0);
cout << "Recieved:" << msg;
*output_done = 1;
close(sock);
pthread_exit(NULL);
}
void io_client()
{
//thread function variables
pthread_t t1,t2;
bool input_done = 1, output_done = 1;
//socket setup variables
struct addrinfo hints, *res;
memset(&hints,0,sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("localhost","8080",&hints,&res);
//setting up structures to pass data to threaded functions
struct thread_args i_args, o_args;
i_args.result = res; i_args.process_done = &input_done;
o_args.result = res; o_args.process_done = &output_done;
while(1)
{
if (output_done)
{
pthread_create(&t2,NULL,output,&o_args);
output_done = 0;
}
if (input_done)
{
pthread_create(&t1,NULL,input,&i_args);
input_done = 0;
}
}
}
int main()
{
io_client();
}
服务器
void server()
{
struct addrinfo hints, *res;
int sock=-1, newsock=-1;
int length, on=1;
char **address_list; int entries = 0;
//fd_set read_fd;
//struct timeval timeout;
char buffer[100];
memset(&hints,0,sizeof hints);
res = NULL;
memset(&res,0,sizeof res);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("localhost","8080",&hints,&res);
sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
bind(sock,res->ai_addr,res->ai_addrlen);
listen(sock,10);
while(1)
{
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN];
socklen_t len;
len = sizeof addr;
newsock = accept(sock,NULL,NULL);
getpeername(newsock,(struct sockaddr*)&addr,&len);
struct sockaddr_in *s = (struct sockaddr_in*)&addr;
inet_ntop(AF_INET,&s->sin_addr,ipstr,sizeof ipstr);
length = 100;
setsockopt(newsock,SOL_SOCKET,SO_RCVLOWAT, (char*)&length,sizeof length);
recv(newsock,buffer,sizeof buffer,0);
cout << buffer << endl;
}
if (newsock != -1)
{
close(newsock);
}
if (sock != -1)
{
close(sock);
}
}
int main()
{
server();
}
看起来您正试图将客户端bind()连接到与服务器相同的端口。这没有必要。更糟糕的是,您正试图绑定到服务器的IP地址,这也是一个更大的问题。通常,对于要调用connect()函数的客户端套接字,您应该只将套接字绑定到端口0和IP 0,这样操作系统就可以为您选择一个随机可用的端口,并为连接使用正确的本地IP地址和适配器。您可以调用getsockname()来发现操作系统在调用connect后为您选择的端口。
如果你让操作系统为你选择客户端端口,你就不需要那个SO_REUSESADDR调用了。尽管如此,您的服务器代码可以在关闭后需要重新启动且连接仍有待关闭的情况下调用它。
此外。您没有检查任何套接字调用的返回值。这可能就是为什么你会得到一些神秘的结果。对bind()的调用更有可能失败,因为您正在指定服务器IP,但connect()成功了,因为它将自动绑定套接字(如果尚未绑定)。
下面是input()函数的清理版本。转换output()函数是留给读者的练习。如果你以我为榜样,你的身体会很好。
void* input(void* ptr)
{
int on = 1;
bool *input_done = ((struct thread_args*)ptr)->process_done;
int ret;
int success = true;
struct sockaddr_in addrLocal = {};
struct addrinfo *res = ((struct thread_args*)ptr)->result;
char msg[256];
int sock = socket(AF_INET, SOCK_STREAM, 0);
success = (sock != -1);
if (success)
{
addrLocal.sin_family = AF_INET;
addrLocal.sin_port = INADDR_ANY; // INADDR_ANY == 0 --> pick a random port for me
addrLocal.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY == 0 --> use all appropriate network
ret = bind(sock,(sockaddr*)&addrLocal,sizeof(addrLocal));
if (ret == -1) perror("bind: ");
success = (ret != -1);
}
if (success)
{
ret = connect(sock,res->ai_addr,res->ai_addrlen);
if (ret == -1) perror("connect: ");
success = (ret != -1);
}
if (success)
{
cin.getline(msg,256);
if (msg[0] == '/') {exit(1);}
ret = send(sock,msg,sizeof msg,0);
if (ret == -1) perror("send: ");
success = (ret != -1);
}
if (success)
{
cout << "You:" << msg << endl;
*input_done = 1;
}
if (sock != -1)
{
close(sock);
sock = -1;
}
return NULL;
}
我想"SO_ REUSEADDR";您提供的套接字选项就是问题所在。
您是否在不关闭客户端套接字的情况下一次又一次地调用该函数?在这种情况下,它不会起作用
此套接字选项的目的是"当已打开的同一地址的套接字处于TIME_WAIT状态时重用该地址,否则将出现上述错误"。
如果您的客户端每次都打开一个新的连接,那么我必须说,您必须更有效地构建代码,并处理套接字关闭场景。
- 将数组的地址分配给变量并删除
- 空基优化子对象的地址
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 如何在c++程序中找到函数的地址
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 被解释为低级别const的const对象的地址
- 将地址分配给本地指针后,公共对象的变量将消失
- 为什么我在leetcode上收到AddressSanitizer:地址0x602000000058上的堆缓冲区溢出错误
- 内联程序集printf将整数解释为地址
- 为什么指针不写入类的地址?
- 如何在C++中获取该对象的类声明中对象的地址?
- 通过按地址访问变量
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 函数名是c中该函数的第一条指令的地址吗
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- ReadProcessMemory() 不适用于像 0x2840C6C68D8 这样的长地址
- CUDA:统一内存和指针地址的更改
- 当我们从/tp地址中添加/减去一个整数时会发生什么
- 如何使用 Boost Asio 在 Android 上获取我的本地 udp IP 地址?