在关闭旧套接字后尝试使用新套接字但没有成功
Trying, without success, to use a new socket after closing the old one
对于课堂作业,我必须在客户端发送 "NEWPORT " 后关闭所有打开的套接字并将 "n-port" 分配给一个新套接字,从那一刻起我必须使用为创建的套接字新的传入连接,但是当我尝试连接到新连接时,客户端的 perror 函数告诉我 "Connection refused"。
服务器代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define MAX_SIZE 512
void manage_connections(int fds,struct sockaddr_in serv_addr, struct sockaddr_in cl_addr, socklen_t cl_len);
int main(int argc, char* argv[]){
struct sockaddr_in serv_addr, cl_addr;
socklen_t cl_len;
int fds=0;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5200);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
fds = socket(AF_INET, SOCK_STREAM, 0);
if(fds<0){
perror("Socket error");
exit(-1);
}
if(bind(fds,(struct sockaddr*)&serv_addr, sizeof(serv_addr))<0){
perror("Bind error");
exit(-1);
}
if((listen(fds,5))<0){
perror("Listen error");
exit(-1);
}
manage_connections(fds,serv_addr, cl_addr, cl_len);
close(fds);
return 0;
}
void manage_connections(int fds,struct sockaddr_in serv_addr, struct sockaddr_in cl_addr, socklen_t cl_len){
char buffer[MAX_SIZE], cl_ip[MAX_SIZE], cl_port[MAX_SIZE], cmd_np[MAX_SIZE], cmd_np_n[MAX_SIZE], cmd_np_str[MAX_SIZE], nport[MAX_SIZE];
int i=0, port=0,fdc=0, nfds;
struct sockaddr_in nserv_addr;
while(1){
cl_len = sizeof(cl_addr);
fdc = accept(fds, (struct sockaddr*)&cl_addr, &cl_len);
if(fdc<0){
perror("Accept error");
exit(-1);
}
do{
for(i=0;i<MAX_SIZE;i++){
buffer[i]=0;
cl_ip[i]=0;
cl_port[i]=0;
cmd_np[i]=0;
cmd_np_n[i]=0;
cmd_np_str[i]=0;
nport[i]=0;
}
read(fdc,buffer, MAX_SIZE);
if(strcmp(buffer,"MYIP")==0){
strcat(cl_ip,inet_ntoa(cl_addr.sin_addr));
write(fdc,cl_ip,strlen(cl_ip));
} else if(strcmp(buffer,"MYPORT")==0){
sprintf(cl_port,"%d", (int)htons(cl_addr.sin_port));
write(fdc, cl_port, strlen(cl_port));
}else{
sscanf(buffer, "%s %d", cmd_np, &port);
sprintf(nport, "%d", port);
if(strcmp(cmd_np,"NEWPORT")==0){
strcat(cmd_np_str, "ACK ");
strcat(cmd_np_str, nport);
write(fdc, cmd_np_str,strlen(cmd_np_str));
close(fds);
close(fdc);
nserv_addr.sin_family=AF_INET;
nserv_addr.sin_port = htons(port);
nserv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
nfds = socket(AF_INET, SOCK_STREAM,0);
if(nfds<0){
perror("New socket error");
exit(-1);
}
if((bind(nfds,(struct sockaddr*)&nserv_addr,sizeof(nserv_addr)))<0){
perror("New bind error");
exit(-1);
}
if((listen(nfds,5))<0){
perror("New listen error");
exit(-1);
}
manage_connections(nfds,nserv_addr, cl_addr, cl_len);
}
}
}while(strcmp(cmd_np,"NEWPORT")!=0);
close(fdc);
}
}
客户代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#define MAX_SIZE 512
int main(int argc, char* argv[]){
struct sockaddr_in cl_addr;
int fdc=0,len=0, res_len=0,i=0;
char buffer[MAX_SIZE];
cl_addr.sin_family = AF_INET;
cl_addr.sin_port = htons(5200);
inet_aton("192.168.10.128",&cl_addr.sin_addr);
fdc = socket(AF_INET, SOCK_STREAM,0);
if(fdc<0){
perror("Accept error");
exit(-1);
}
if((connect(fdc, (struct sockaddr*)&cl_addr, sizeof(cl_addr)))<0){
perror("Connect error");
exit(-1);
}
while(1){
write(1, "Inserire uno tra i comandi MYIP, MYPORT, NEWPORT:\n", strlen("Inserire uno tra i comandi MYIP, MYPORT, NEWPORT:\n"));
len = read(1, buffer, MAX_SIZE);
buffer[len-1] = '[=11=]';
write(fdc, buffer, strlen(buffer)+1);
for(i=0;i<MAX_SIZE;i++)
buffer[i]=0;
res_len = read(fdc,buffer,MAX_SIZE);
write(1,"\nResponso: ",strlen("\nResponso: "));
write(1, buffer, res_len);
write(1,"\n",strlen("\n"));
}
close(fdc);
return 0;
}
您的客户端已硬编码为 5200 端口号。如果您使用 NEWPORT
将服务器切换到一个新服务器,服务器将不再侦听 5200,因为您正在关闭原来的侦听套接字并打开一个新套接字。
因此,当您再次 运行 您的客户端时,它会收到连接被拒绝 因为 现在没有人在监听您正在连接的套接字。
我看到的最佳解决方法是在客户端添加指定端口号作为命令行参数的功能。
您的代码的其他问题:
您假设您将在每次调用 write
时发送整个缓冲区内容,并且您将在每个 read
上接收整个缓冲区内容。在像这样的玩具程序中,它可以工作,但 TCP 不保证。
您的 manage_connections
函数是递归的,这似乎是一个糟糕的结构。足够多的调用,你会 运行 出栈。
没有退出客户端程序的内置方法。您可以按 Ctrl-C 中止,但在 NEWPORT
情况下,它不会检测到服务器刚刚关闭连接(因为它正在从终端读取)。
您实际上是在从标准输出文件中读取。如果您连接到终端,这通常会起作用,但在其他情况下不起作用,也不是一件值得依赖的好事。
您将 cl_addr
和 cl_len
变量传递给 manage_connections
,但这是毫无意义的。调用者不会在调用之前初始化它们或在调用之后使用它们。它们应该只在函数内部声明。
对于课堂作业,我必须在客户端发送 "NEWPORT " 后关闭所有打开的套接字并将 "n-port" 分配给一个新套接字,从那一刻起我必须使用为创建的套接字新的传入连接,但是当我尝试连接到新连接时,客户端的 perror 函数告诉我 "Connection refused"。
服务器代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define MAX_SIZE 512
void manage_connections(int fds,struct sockaddr_in serv_addr, struct sockaddr_in cl_addr, socklen_t cl_len);
int main(int argc, char* argv[]){
struct sockaddr_in serv_addr, cl_addr;
socklen_t cl_len;
int fds=0;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5200);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
fds = socket(AF_INET, SOCK_STREAM, 0);
if(fds<0){
perror("Socket error");
exit(-1);
}
if(bind(fds,(struct sockaddr*)&serv_addr, sizeof(serv_addr))<0){
perror("Bind error");
exit(-1);
}
if((listen(fds,5))<0){
perror("Listen error");
exit(-1);
}
manage_connections(fds,serv_addr, cl_addr, cl_len);
close(fds);
return 0;
}
void manage_connections(int fds,struct sockaddr_in serv_addr, struct sockaddr_in cl_addr, socklen_t cl_len){
char buffer[MAX_SIZE], cl_ip[MAX_SIZE], cl_port[MAX_SIZE], cmd_np[MAX_SIZE], cmd_np_n[MAX_SIZE], cmd_np_str[MAX_SIZE], nport[MAX_SIZE];
int i=0, port=0,fdc=0, nfds;
struct sockaddr_in nserv_addr;
while(1){
cl_len = sizeof(cl_addr);
fdc = accept(fds, (struct sockaddr*)&cl_addr, &cl_len);
if(fdc<0){
perror("Accept error");
exit(-1);
}
do{
for(i=0;i<MAX_SIZE;i++){
buffer[i]=0;
cl_ip[i]=0;
cl_port[i]=0;
cmd_np[i]=0;
cmd_np_n[i]=0;
cmd_np_str[i]=0;
nport[i]=0;
}
read(fdc,buffer, MAX_SIZE);
if(strcmp(buffer,"MYIP")==0){
strcat(cl_ip,inet_ntoa(cl_addr.sin_addr));
write(fdc,cl_ip,strlen(cl_ip));
} else if(strcmp(buffer,"MYPORT")==0){
sprintf(cl_port,"%d", (int)htons(cl_addr.sin_port));
write(fdc, cl_port, strlen(cl_port));
}else{
sscanf(buffer, "%s %d", cmd_np, &port);
sprintf(nport, "%d", port);
if(strcmp(cmd_np,"NEWPORT")==0){
strcat(cmd_np_str, "ACK ");
strcat(cmd_np_str, nport);
write(fdc, cmd_np_str,strlen(cmd_np_str));
close(fds);
close(fdc);
nserv_addr.sin_family=AF_INET;
nserv_addr.sin_port = htons(port);
nserv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
nfds = socket(AF_INET, SOCK_STREAM,0);
if(nfds<0){
perror("New socket error");
exit(-1);
}
if((bind(nfds,(struct sockaddr*)&nserv_addr,sizeof(nserv_addr)))<0){
perror("New bind error");
exit(-1);
}
if((listen(nfds,5))<0){
perror("New listen error");
exit(-1);
}
manage_connections(nfds,nserv_addr, cl_addr, cl_len);
}
}
}while(strcmp(cmd_np,"NEWPORT")!=0);
close(fdc);
}
}
客户代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#define MAX_SIZE 512
int main(int argc, char* argv[]){
struct sockaddr_in cl_addr;
int fdc=0,len=0, res_len=0,i=0;
char buffer[MAX_SIZE];
cl_addr.sin_family = AF_INET;
cl_addr.sin_port = htons(5200);
inet_aton("192.168.10.128",&cl_addr.sin_addr);
fdc = socket(AF_INET, SOCK_STREAM,0);
if(fdc<0){
perror("Accept error");
exit(-1);
}
if((connect(fdc, (struct sockaddr*)&cl_addr, sizeof(cl_addr)))<0){
perror("Connect error");
exit(-1);
}
while(1){
write(1, "Inserire uno tra i comandi MYIP, MYPORT, NEWPORT:\n", strlen("Inserire uno tra i comandi MYIP, MYPORT, NEWPORT:\n"));
len = read(1, buffer, MAX_SIZE);
buffer[len-1] = '[=11=]';
write(fdc, buffer, strlen(buffer)+1);
for(i=0;i<MAX_SIZE;i++)
buffer[i]=0;
res_len = read(fdc,buffer,MAX_SIZE);
write(1,"\nResponso: ",strlen("\nResponso: "));
write(1, buffer, res_len);
write(1,"\n",strlen("\n"));
}
close(fdc);
return 0;
}
您的客户端已硬编码为 5200 端口号。如果您使用 NEWPORT
将服务器切换到一个新服务器,服务器将不再侦听 5200,因为您正在关闭原来的侦听套接字并打开一个新套接字。
因此,当您再次 运行 您的客户端时,它会收到连接被拒绝 因为 现在没有人在监听您正在连接的套接字。
我看到的最佳解决方法是在客户端添加指定端口号作为命令行参数的功能。
您的代码的其他问题:
您假设您将在每次调用
write
时发送整个缓冲区内容,并且您将在每个read
上接收整个缓冲区内容。在像这样的玩具程序中,它可以工作,但 TCP 不保证。您的
manage_connections
函数是递归的,这似乎是一个糟糕的结构。足够多的调用,你会 运行 出栈。没有退出客户端程序的内置方法。您可以按 Ctrl-C 中止,但在
NEWPORT
情况下,它不会检测到服务器刚刚关闭连接(因为它正在从终端读取)。您实际上是在从标准输出文件中读取。如果您连接到终端,这通常会起作用,但在其他情况下不起作用,也不是一件值得依赖的好事。
您将
cl_addr
和cl_len
变量传递给manage_connections
,但这是毫无意义的。调用者不会在调用之前初始化它们或在调用之后使用它们。它们应该只在函数内部声明。