user18503064
user18503064

Reputation:

Why do tcp applications send a reset segment after close()ing the connection with data unread in the tcp receive buffer?

I've noticed that whenever I call close() from a TCP application and unread data is still inside the TCP receiving buffer the application sends a RST segment.

1)Is this the default behaviour of any TCP protocol or is OS dependent ? (I'm working on Mac Os);

2)How can I prevent the application from sending a RST segment and allow it to send a FIN even with unread data in the buffer ? Is there any way to flush this data and make the app think it has read anything?

3)Why did tcp engineers decide to implement this behaviour ?

Here is the client and server code respectively. I let the client close the connection with the '#' character still in the TCP receiving buffer on purpose :


int main(){


int sockfd;
struct sockaddr_in servaddr, cli;
//CREATE SOCKET
sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }
    //Server ip and port
    bzero(&servaddr, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr.s_addr);
    servaddr.sin_port = htons(PORT_SERVER);
    
    

if (connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
        if(errno == ECONNREFUSED){
            printf("Host service inactive on the specified port \n");
        }
        else if(errno == ETIMEDOUT || errno == EHOSTUNREACH){
            printf("Connection timed out ! The server could not be reached \n");
        }else{
            printf("Connection could not be established \n");
        }
        errno = 0;
        close(sockfd);
        return 0;
    }


char buff;
while((read(sockfd,&buff,sizeof(buff))) > 0){
     if(buff =='\n'){
     write(1,&buff,sizeof(buff));
     break;
     }
     write(1,&buff,sizeof(buff));
};
close(sockfd);
return 0;
}



int main(){

 int sockfd, connfd;
 unsigned int len;
 struct sockaddr_in servaddr, cli;

//SOCKET CREATION
sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }


bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr.s_addr);
servaddr.sin_port = htons(PORT);

if ((bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) != 0) {
        printf("socket bind failed...\n");
        exit(0);
    }

if ((listen(sockfd,128)) != 0) {
        printf("Listen failed...\n");
        exit(0);
    }

for(;;){

len = sizeof(cli);

connfd = accept(sockfd,(struct sockaddr *)&cli, &len);

if (connfd < 0) {
        printf("server accept failed...\n");
        exit(0);
    }

char hello[7]={'H','e','l','l','o','\n','#'};
write(connfd,hello,sizeof(hello));
char buffr;
int r;
while( (r=read(connfd,&buffr,sizeof(buffr))) > 0 ){}
       close(connfd);
};

return 0;

}

Upvotes: 1

Views: 602

Answers (1)

To send a FIN use shutdown(connfd, SOCK_WR). Then, you keep reading until there is nothing left to read (read or recv returns 0), which means the other computer also sent a FIN. Then, you close the socket.

To my knowledge it is like this on all operating systems. RST is sent if another computer sends a packet to a nonexistent socket. By sending an RST when you close the socket, even if another packet wasn't received yet, your computer is being very slightly more polite by telling the other computer it's not listening any more.

Upvotes: 2

Related Questions