2010-06-27 52 views
1

我有這個編,只是一個簡單的服務器和客戶端連接的骨架。我會讓它聊天。 (不介意線程FUNC和信號..)linux套接字:客戶端之前服務器退出

服務器:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include <time.h> 
#include <string.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <pthread.h> 


#define BUFFERSIZE 512 
#define TIMESIZE 32 
#define QUIT "!quit" 


// thread pou diavazei 
void *readthread(void *argp); 
// katharizei ligo prin kleisei to programma 
void progreset(); 
// kleinei to prog me ctrl-c 
void sigexit(); 


int sock, endchat; 
char username1[50]; 
pthread_t thrread; 


int main(int argc, char** argv) { 
    int port, s; 
    char username[50]; 
    struct sockaddr_in server, client; 
    struct sockaddr *serverptr, *clientptr; 
    unsigned int clientlen; 
    char buf[BUFFERSIZE]; 
    int len; 
    time_t sec; 
    char timestr[TIMESIZE]; 


    signal(SIGPIPE, SIG_IGN); 
    signal(SIGINT, sigexit); 


    if (argc != 3) { 
     printf("Error: Wrong arguments\n"); 
     printf("Usage: %s <username> <port>\n", argv[0]); 
     return -1; 
    } 

    strcpy(username, argv[1]); 
    port = atoi(argv[2]); 

    // ftiaxno to socket 
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
     perror("Failed to create socket"); 
     return -1; 
    } 
    server.sin_family = AF_INET; 
    server.sin_addr.s_addr = htonl(INADDR_ANY); 
    server.sin_port = htons((short)port); 
    if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) { 
     perror("Failed to bind socket"); 
     return -1; 
    } 

    if (listen(s, 5) != 0) { 
     perror("Error in listen()"); 
     return -1; 
    } 

    clientptr = (struct sockaddr *)&client; 
    clientlen = sizeof(client); 

    // perimeno sindesi apo ton pelati 
    printf("Accepting connections on port %d..\n", port); 
    if ((sock = accept(s, clientptr, &clientlen)) < 0) { 
     perror("Error in accept()"); 
     return -1; 
    } 

    // pairno to ip tou pelati 
    if (getpeername(sock, (struct sockaddr *)&client, &clientlen) < 0) { 
     printf("Accepted connection\n"); 
    } else { 
     printf("Accepted connection from %s\n", inet_ntoa(client.sin_addr)); 
    } 

    // stelno kai pairno ta usernames 
    bzero(buf, sizeof(buf)); 
    strcpy(buf, username); 
    if (write(sock, buf, sizeof(buf)) < 0) { 
     perror("write1"); 
     return -1; 
    } 

    bzero(buf, sizeof(buf)); 
    if (read(sock, buf, sizeof(buf)) < 0) { 
     perror("read1"); 
     return -1; 
    } 
    strcpy(username1, buf); 

    printf("Chatting with %s..\n\n", username1); 








    progreset(); 
    return 0; 
} 


void *readthread(void *argp) { 
    char buf[BUFFERSIZE]; 
    char timestr[TIMESIZE]; 
    int len; 
    time_t sec; 
    struct tm *timeinfo; 


    while (1) { 

    } 



    endchat = 1; 
    pthread_exit(0); 
} 


void progreset() { 
    printf("\nExiting..\n"); 
    close(sock); 
} 


void sigexit() { 
    printf("test\n"); 
    close(sock); 
    signal(SIGINT, SIG_DFL); 
    kill(getpid(),SIGINT); 
    printf("ok\n"); 

    exit(0); 
} 

客戶

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <pthread.h> 


#define BUFFERSIZE 512 
#define TIMESIZE 32 
#define QUIT "!quit" 


// thread pou diavazei 
void *readthread(void *argp); 
// katharizei ligo prin kleisei to programma 
void progreset(); 
// kleinei to prog me ctrl-c 
void sigexit(); 


int sock, endchat; 
char username1[50]; 
pthread_t thrread; 


int main(int argc, char** argv) { 
    int port; 
    char username[50]; 
    struct sockaddr_in server, client; 
    struct sockaddr *serverptr, *clientptr; 
    unsigned int serverlen; 
    struct hostent *rem; 
    char buf[BUFFERSIZE]; 
    int len; 
    time_t sec; 
    char timestr[TIMESIZE]; 


    signal(SIGPIPE, SIG_IGN); 
    signal(SIGINT, sigexit); 


    if (argc != 4) { 
     printf("Error: Wrong arguments\n"); 
     printf("Usage: %s <username> <ip address> <port>\n", argv[0]); 
     return -1; 
    } 

    strcpy(username, argv[1]); 
    port = atoi(argv[3]); 

    // ftiaxno to socket 
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
     perror("Failed to create socket"); 
     return -1; 
    } 
    rem = gethostbyname(argv[2]); 
    server.sin_family = AF_INET; 
    bcopy((char*)rem->h_addr, (char*)&server.sin_addr, rem->h_length); 
    server.sin_port = htons((short)port); 
    serverptr = (struct sockaddr *)&server; 
    serverlen = sizeof(server); 

    // kano connect me to server 
    if (connect(sock, serverptr, serverlen) < 0) { 
     perror("Failed to connect"); 
     return -1; 
    } 

    // pairno kai stelno ta usernames 
    bzero(buf, sizeof(buf)); 
    if (read(sock, buf, sizeof(buf)) < 0) { 
     perror("read1"); 
     return -1; 
    } 
    strcpy(username1, buf); 

    bzero(buf, sizeof(buf)); 
    strcpy(buf, username); 
    if (write(sock, buf, sizeof(buf)) < 0) { 
     perror("write1"); 
     return -1; 
    } 

    printf("Chatting with %s..\n\n", username1); 







    sleep(1); 










    progreset(); 
    return 0; 
} 


void *readthread(void *argp) { 
    char buf[BUFFERSIZE]; 
    char timestr[TIMESIZE]; 
    int len; 
    time_t sec; 
    struct tm *timeinfo; 

    while (1) { 
    } 
    endchat = 1; 
    pthread_exit(0); 
} 


void progreset() { 
    printf("\nExiting..\n"); 
    close(sock); 
} 


void sigexit() { 
    printf("test\n"); 
    close(sock); 
    signal(SIGINT, SIG_DFL); 
    kill(getpid(),SIGINT); 
    printf("ok\n"); 

    exit(0); 
} 

在main()FUNC按鍵,服務器只是等待連接,然後退出。客戶端在連接睡眠1秒後,然後結束。

當我運行這一點,像./server服務器1234 然後./client客戶端本地主機1234 都正常退出,但是當我第二次運行該服務器,它說未能綁定套接字:已在使用中的地址。

有什麼不對? 服務器必須在客戶端之後始終退出嗎?

獎金問:我想每個程序有兩個線程,一個讀取一個寫入。他們可以在同一個插座上操作嗎?

非常感謝你

+0

您是否刪除了.sock文件? – 2010-06-27 16:47:07

+0

.sock ??我沒有.sock文件 – pvinis 2010-06-27 16:51:58

+0

也許我錯了,但是當你創建一個套接字,並且你把它叫做MYSOCK時,它會在你的目錄MYSOCK.sock中創建一個文件(如果你沒有指定)。如果您發現將其刪除,然後重試。如果你找不到我錯了! 編輯可能我錯了,因爲我使用AF_UNIX套接字。對不起 – 2010-06-27 16:55:10

回答

1

請確保在退出之前在插座上調用closesocket(s)。
也請在打開之前嘗試使用setsockopt SO_REUSEADDR。

Here是Linux Sockets上的一個很好的教程。

當然,如果你發佈了一些代碼,它會使它更容易。

您可以在同一個套接字上讀寫。

我懷疑你得到「Address in use error」(EADDRINUSE)
你可以使用bind API函數將一個地址(一個接口和一個端口)綁定到一個套接字端點上。您可以在服務器設置中使用此功能來限制傳入連接可能的接口。您也可以在客戶端設置中使用此功能來限制應該用於傳出連接的接口。
綁定的最常見用途是將端口號與服務器相關聯,並使用通配符地址(INADDR_ANY),該地址允許任何接口用於傳入連接。
綁定常遇到的問題是試圖綁定已經在使用的端口。缺點是沒有活動的套接字可能存在,但綁定到端口仍然被禁止(綁定返回EADDRINUSE),這是由TCP套接字TIME_WAIT狀態引起的。這個狀態在關閉後會保持一個插槽兩到四分鐘。
TIME_WAIT狀態退出後,套接字被刪除,並且地址可以被反彈而沒有問題。
等待TIME_WAIT完成可能很煩人,特別是如果您正在開發套接字服務器,並且您需要停止服務器進行更改並重新啓動它。幸運的是,有一種解決TIME_WAIT狀態的方法。您可以將SO_REUSEADDR套接字選項應用於套接字,以便可以立即重新使用該端口。 在綁定地址之前,我使用SO_REUSEADDR選項調用setsockopt。要啓用地址重用,我將整數參數(on)設置爲1(否則,您可以將其設置爲0以禁用地址重用)。

使用SO_REUSEADDR套接字選項

int sock, ret, on; 
struct sockaddr_in servaddr; 

/* Create a new stream (TCP) socket */ 
sock = socket(AF_INET, SOCK_STREAM, 0): 

/* Enable address reuse */ 
on = 1; 
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 

/* Allow connections to port 8080 from any available interface */ 
memset(&servaddr, 0, sizeof(servaddr)); 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
servaddr.sin_port = htons(45000); 

/* Bind to the address (interface/port) */ 
ret = bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)); 

後您已應用SO_REUSEADDR套接字選項避免「地址在使用」錯誤,綁定API函數總是允許地址的立即重用。

+0

我是否在綁定之前放置這個? – pvinis 2010-06-27 22:06:31

+0

@pvinis _我更新了我的帖子,告訴我如何使用SO_REUSEADDR – 2010-06-27 22:28:55

+0

非常感謝!我注意到幾秒鐘後端口是空閒的,但我不知道TIME_WAIT。 setsockopt和SO_REUSEADDR解決了我的問題。 – pvinis 2010-06-29 09:04:47

相關問題