1

I am making an C++ socket application.

It connects with android device.

When I close connection on phone during receiving data..

Framebuffer Ready
[PCast] Waiting for connection
[PCast] Connected
....
[PCast] Data arrived
[PCast] *** Downloading mode ***
[PCast] Downloading file. Please wait...
[PCast:sock2file] Downloading...
[PCast:sock2file] Disconnection detected. Exiting sock2file.
[PCast] Disconnected
(Close client socket descriptor)

[PCast] Waiting for connection

It looks like the connection is successfully closed. but When I restart server..

Framebuffer Ready
[PCast] Socket bind failed: Address already in use

When my android app tries to connect(Server is not running), It says that it is connected to server.(Data transfer does not working)

When I run

netstat -anp

I can see that my port is in CLOSE_WAIT status.

...
tcp        1      0 172.30.1.3:5520         172.30.1.2:47144        CLOSE_WAIT  -               
tcp        0      0 172.30.1.3:22           172.30.1.1:50598        ESTABLISHED -               
tcp        1      0 172.30.1.3:5520         172.30.1.2:47202        CLOSE_WAIT  -     
...

Code(Omitting logging, korean comment...):

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <ctime>
#include <cmath>
#include <linux/fb.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "imgs.h"

using std::string;

#define FBDEV "/dev/fb0"
int scrsize, width, height;
int basesize;

#define CMD_GET_IP_ETH "/home/pi/scripts/ip.sh eth"
#define CMD_GET_IP_WLN "/home/pi/scripts/ip.sh wlan"

#define getPercent(total, current) (current / total) * 100
#define getLargeValue(org, b) (org * b) / 320

short *display;
short org[320*240];

//Convert images to char map
char strmap[128][5*5];

bool debug = false;

void cons_log(string message){
    printf("%s\n", message.c_str());
}

void drawPixel(int x, int y, short col){
    if((width * y + x) < width * height){
        display[width*y+x] = col;
    }
}

short mkcolor(int r, int g, int b){
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}

void writeImage(int sx, int sy, char img[], int size){
    size *= basesize;
    for(int y = 0; y < 5; y++) for(int x = 0; x < 5; x++){
        int prtx = sx + x * size, prty = sy + y * size;
        for(int rx = 0; rx < size; rx++) for(int ry = 0; ry < size; ry++){
            drawPixel(prtx + rx, prty + ry, img[5*y+x] * 0xffff);
        }
    }
}

void writeImage(char img[], int size){
    int centerX = width / 2 - 5 * size / 2;
    int centerY = height / 2 - 5 * size / 2;
    writeImage(centerX, centerY, img, size);
}

void writeMessage(int x, int y, string s, int size){
    size *= basesize;
    y += height / 2 - size * 5;
    for(int i = 0; i < s.length(); i++){
        int prtx = x + i * (size * 5);
        writeImage(prtx, y, strmap[s.at(i)], size / basesize);
    }
}

void writeMessage(int y, string s, int size){
    size *= basesize;
    int x = width / 2 - ((s.length()) * (size * 5)) / 2;
    y += height / 2 - size * 5;
    for(int i = 0; i < s.length(); i++){
        int prtx = x + i * (size * 5);
        writeImage(prtx, y, strmap[s.at(i)], size / basesize);
    }
}

void writeMessage(string s, int size){
    writeMessage(0, s, size);
}

void clear(){
    for(int i = 0; i < width * height; i++) display[i] = 0;
}

#define img_height(size) size * 5 * basesize

void printStandBy(bool connected){
    clear();
    writeMessage("pcast", 2);
    char buffer[100];
    FILE* pipe;
    pipe = popen("sh /home/pi/scripts/ip.sh eth", "r");
    fgets(buffer, 100, pipe);
    pclose(pipe);
    writeMessage(img_height(2) + 10, buffer, 1);
    memset(buffer, 0, 100);
    pipe = popen("sh /home/pi/scripts/ip.sh wlan", "r");
    fgets(buffer, 100, pipe);
    pclose(pipe);
    writeMessage(img_height(2) * 2 + 10, buffer, 1);
    if(connected){
        writeMessage(img_height(2) * 3 + 10, "connected", 1);
    }else{
        writeMessage(img_height(2) * 3 + 10, "not connected", 1);
    }
}

void printDownloading(){
    clear();
    writeImage(IMG_DOWNLOAD, 2);
    writeMessage(img_height(2) + 10, "downloading", 1);
}

int server_sockfd, client_sockfd;

void endHandler(int signo){
    if(client_sockfd != NULL) close(client_sockfd);
    if(server_sockfd != NULL) close(server_sockfd);
    exit(0);
}

bool sock2file(int socket){
    system("sudo rm /tmp/tmp");
    int count = 0;
    int file = open("/tmp/tmp", O_WRONLY | O_CREAT, 0644);
    int totalBytes = 0;
    while(true){
        char buffer[1024*1024];
        int recvBytes = recv(socket, buffer, 1024*1024, MSG_DONTWAIT);
        if(recvBytes == 0){
            return false;
        }else if(recvBytes == -1){
            if(count == 5000){
                char send[] = {1, NULL};
                write(socket, send, 1);
                break;
            }else{
                usleep(1000);
                count++;
            }
        }else{
            count = 0;
            char send[] = {0, NULL};
            write(socket, send, 1);
            write(file, buffer, recvBytes);
            totalBytes += recvBytes;
            //byte to B/KB/MB/GB
            int displaySize = totalBytes;
            char sizeUnit = 'b';
            if(displaySize > 1024){
                displaySize /= 1024;
                sizeUnit = 'k';
            }
            if(displaySize > 1024){
                displaySize /= 1024;
                sizeUnit = 'm';
            }
            //Print current status
            char buf[100];
            sprintf(buf, "received %d%c    ", displaySize, sizeUnit);
            string status(buf);
            writeMessage(0, img_height(2) * 2 + 10, status, 1);
        }
    }
    return true;
}

int main(int argc, char **argv){
    if(argc == 2){
        if(!strcmp(argv[0], "d")) debug = true;
    }
    signal(SIGTERM, endHandler);
    signal(SIGINT, endHandler);
    memcpy(strmap['0'], IMG_0, 5*5);
    memcpy(strmap['1'], IMG_1, 5*5);
    memcpy(strmap['2'], IMG_2, 5*5);
    memcpy(strmap['3'], IMG_3, 5*5);
    memcpy(strmap['4'], IMG_4, 5*5);
    memcpy(strmap['5'], IMG_5, 5*5);
    memcpy(strmap['6'], IMG_6, 5*5);
    memcpy(strmap['7'], IMG_7, 5*5);
    memcpy(strmap['8'], IMG_8, 5*5);
    memcpy(strmap['9'], IMG_9, 5*5);
    memcpy(strmap['.'], IMG_DOT, 5*5);
    memcpy(strmap[':'], IMG_DOBULE_DOT, 5*5);
    memcpy(strmap['a'], IMG_A, 5*5);
    memcpy(strmap['b'], IMG_B, 5*5);
    memcpy(strmap['c'], IMG_C, 5*5);
    memcpy(strmap['d'], IMG_D, 5*5);
    memcpy(strmap['e'], IMG_E, 5*5);
    memcpy(strmap['f'], IMG_F, 5*5);
    memcpy(strmap['g'], IMG_G, 5*5);
    memcpy(strmap['h'], IMG_H, 5*5);
    memcpy(strmap['i'], IMG_I, 5*5);
    memcpy(strmap['j'], IMG_J, 5*5);
    memcpy(strmap['k'], IMG_K, 5*5);
    memcpy(strmap['m'], IMG_M, 5*5);
    memcpy(strmap['n'], IMG_N, 5*5);
    memcpy(strmap['l'], IMG_L, 5*5);
    memcpy(strmap['o'], IMG_O, 5*5);
    memcpy(strmap['p'], IMG_P, 5*5);
    memcpy(strmap['q'], IMG_Q, 5*5);
    memcpy(strmap['r'], IMG_R, 5*5);
    memcpy(strmap['s'], IMG_S, 5*5);
    memcpy(strmap['t'], IMG_T, 5*5);
    memcpy(strmap['u'], IMG_U, 5*5);
    memcpy(strmap['v'], IMG_V, 5*5);
    memcpy(strmap['w'], IMG_W, 5*5);
    memcpy(strmap['x'], IMG_X, 5*5);
    memcpy(strmap['y'], IMG_Y, 5*5);
    memcpy(strmap['z'], IMG_Z, 5*5);
    memcpy(strmap[' '], IMG_SPACE, 5*5);

    //Framebuffer Setup
    int fb_fd;
    fb_fd = open(FBDEV, O_RDWR);
    if(!fb_fd){
        exit(1);
    }
    fb_var_screeninfo fvsInfo;
    if(ioctl(fb_fd, FBIOGET_VSCREENINFO, &fvsInfo)){
        exit(1);
    }
    width = fvsInfo.xres;
    height = fvsInfo.yres;
    scrsize = width * height * 2;
    basesize = (width + height) / 2 / 100;
    display = (short *)mmap(0, scrsize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if((int *)display == -1){
        exit(1);
    }
    printStandBy(false);
    //Socket setup
    int state, client_len;
    int pid;
    struct sockaddr_in siClient, siServer;
    state = 0;
    client_len = sizeof(siClient);
    if((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        exit(0);
    }
    bzero(&siServer, sizeof(siServer));
    siServer.sin_family = AF_INET;
    siServer.sin_addr.s_addr = htonl(INADDR_ANY);
    siServer.sin_port = htons(5520);
    state = bind(server_sockfd , (struct sockaddr *)&siServer, sizeof(siServer));
    if (state == -1){
        exit(0);
    }
    state = listen(server_sockfd, 5);
    if (state == -1){
        exit(0);
    }
    while(1){
        client_sockfd = accept(server_sockfd, (struct sockaddr *)&siClient,
                               &client_len);
        if (client_sockfd == -1){
            exit(0);
        }
        printStandBy(true);
        int avg;
        int orgtime = time(NULL);
        while(1){
            char datainfo[2];
            int recvBytes = recv(client_sockfd, datainfo, 2, MSG_WAITALL);
            if(recvBytes == 0 || recvBytes == -1) break;
            else{
                system("sudo killall omxplayer*");
                write(client_sockfd, datainfo, 2);
                //*** Downloading mode ***
                printDownloading();
                bool status = sock2file(client_sockfd);
                if(!status) break;
                printStandBy(true);
                system("nohup omxplayer /tmp/tmp &");
            }
        }
        printStandBy(false);
        close(client_sockfd);
    }
}
  • The program wasn't crashed. I exited with Ctrl + C(As you can see, there's a handler for Ctrl + C, kill signal...)

  • My android app closes connection when application exits.

  • Sorry for poor english.

Gippeumi
  • 251
  • 2
  • 18
  • Your English is fine. Why don't you leave the Korean comments in the code for anyone here who can read Korean (I'm *sure* you're not the only one)? – dfeuer Jun 13 '14 at 14:30
  • If I remember my classes correctly, it takes up to 120 second to close a listening socket, so you need to let some time pass between each application restart. Other socket types (not tcp) doesn't have this issue iirc. Edit: check this answer you might able to use the same flag for the socket: http://stackoverflow.com/questions/10619952/how-to-completely-destroy-a-socket-connection-in-c – Lorenzo Boccaccia Jun 13 '14 at 14:32
  • @LorenzoBoccaccia It happened again :( – Gippeumi Jun 13 '14 at 14:58

2 Answers2

4

When the remote end closes the connection abnormally the port is tied up in TIME_WAIT state and you cannot reuse (i.e. rebind) it unless the timeout has expired. You could reduce the timeout, set socket option SO_REUSEADDR to force reusing the port or just wait until the port is released when restarting your server.

jasal
  • 1,044
  • 6
  • 14
1

Setting the SO_REUSEADDR option on the listening socket will force the OS to allow you to bind to the port even if it is in use. This will resolve your symptom, but not the underlying cause, and thus is susceptible to problems if there really are multiple servers running.

If your server initiates sending FIN on the TCP socket before a FIN arrived from the client, then the TCP state machine mandates it to transition to the TIME_WAIT state. In your case, you may be shutting down your server with CtrlC before the client actually closes the connection, and thus the server is initiating the FIN.

Since you are forcibly shutting down the server, you probably would rather issue RESET on the TCP socket instead. On most TCP stacks, this is accomplished by enabling the SO_LINGER option with a timeout value of 0. You can also set this for the case when you have timed out waiting for the client to send something.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • I tried SO_LINGER with timeout 0. But still it says that Address in use. I see this message on this case: When I end connection during sending big file.(After small file is sent) – Gippeumi Jun 13 '14 at 15:31
  • You only do the SO_LINGER solution in the case you want to close the connection forcibly. It is not an option you enable all the time. The forcibly conditions in the code you posted in your server are during [Ctrl][C] and after timeout (your 5 second wait). – jxh Jun 13 '14 at 20:33
  • 5 second timeout in sock2file()? It is about data transfer. If there's no more data for 5 seconds, it plays the downloaded video. – Gippeumi Jun 13 '14 at 22:53
  • Okay, the logic is a little convoluted. sock2file() can treat non EAGAIN errors as a timeout condition. Basically, it seems there are still cases where you can call close(client_sockfd) in main() before client has sent FIN. You need to check your error cases carefully, and do the SO_LINGER with 0 timeout on those cases where you did not get a FIN from client. – jxh Jun 13 '14 at 23:11
  • I fixed problem. The problem was dbus-daemon using my port. I killed it and now I can restart server without reboot. – Gippeumi Jun 14 '14 at 00:51
  • Hi! I have this issue with c# sockets. as you say the problem comes from DC process. can u help me please if you know about c# sockets? when i DC form one side, other side receive 0 bytes. then what? am i supposed to call DC then in other side? – Mokhabadi Jan 10 '20 at 10:36