我的NDK學習開發筆記(四)

TCP Socket編程

前言

android程序開發中可能需要使用socket訪問網絡,在android開發中,網絡訪問必須在子線程中,但本人發現當使用C++訪問網絡並不需要在子線程中。java使用socket很多人都會,但是C++使用socket可能就沒有多少android程序會使用。本篇記錄的是本人使用C++socket。

一、開發中的問題記錄

1.創建socket。socket(PF_INET, SOCK_STREAM,0),參數:PF_INET表示ipv4協議,SOCK_STREAM 表示使用TCP協議(若使用UDP協議則是SOCK_DGRAM),flags:一般設置爲0。返回socket標記(int)。
2.綁定socket。創建服務socket需要的步驟。bind(sock_fd, (structsockaddr*) &my_addr, sizeof(structsockaddr)),參數:socket標記,綁定地址結構體(包含ip地址和端口號),綁定地址數據長度。
3.開始監聽。創建服務socket需要的步驟。listen(sock_fd,BACKLOG),參數:socket標記,最大同時連接請求數。
4.等待連接。創建服務socket需要的步驟。accept(sock_fd, (structsockaddr*) &my_addr, &sin_size)),參數:socket標記,監聽地址結構體,監聽地址數據長度。返回:客戶socket連接。注:在close(socket)時,並不會中斷該操作。建議使用select來監聽
5.連接ip地址。創建客戶端socket需要的步驟。connect(sock_fd, (structsockaddr*) &serv_addr, sizeof(structsockaddr))
6.發送數據。send(sock_fd, buff, len,0),參數:socket標記,存放要發送數據的緩衝區,實際發送的長度,flags。
7.接收數據。recv(sock_fd, buff,MAXDATASIZE,0),MAXDATASIZE:讀取數據的長度。
8.關閉socket。close(sock_fd),注:如果是服務socket,不但得關閉sock_fd,同時客戶socket連接也需要關閉。
9.需要引入的文件
#include <arpa/inet.h>//設置ip地址
#include <unistd.h>//關閉socket

二、學習時編寫的代碼

1.創建服務socket

#define BACKLOG 10    //最大同時連接請求數
int isServe = 0;
int serve_sock_fd;//服務socket

void startServe() {
    LOGI("--startServe--");
    if (isServe) {
        LOGI("--已開啓Serve--");
        return;
    }
    isServe = 1;
    int sock_fd;//記錄socket
    //1.創建socket
    /**
     * PF_INET ipv4協議
     * SOCK_STREAM 表示使用TCP協議
     * SOCK_DGRAM 表示使用UDP協議
    */
    if (-1 == (sock_fd = socket(PF_INET, SOCK_STREAM, 0))) {
        LOGI("--socket創建出錯--");
    } else
        LOGI("--socket創建成功--");
    //2.綁定socket
    struct sockaddr_in my_addr;
    my_addr.sin_family = PF_INET;//設置協議
    my_addr.sin_port = htons(SERVPORT);//設置端口
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//綁定到所有地址 inet_addr("127.0.0.1");//設置127只能本地訪問
    if (bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
        LOGI("--綁定socket失敗--");
    } else
        LOGI("--綁定socket成功--");
    //3.開始監聽
    if (listen(sock_fd, BACKLOG) == -1) {
        LOGI("--socket監聽失敗--");
        isServe = 0;
    } else {
        LOGI("--socket監聽成功--");
        serve_sock_fd = sock_fd;
    }
    while (isServe) {
        LOGI("--socket開始等待連接--");
        int client_fd;
        socklen_t sin_size = sizeof(struct sockaddr_in);
        if ((client_fd = accept(sock_fd, (struct sockaddr *) &my_addr, &sin_size)) == -1) {
            //儘量不要使用NULL
            //if ((client_fd = accept(sock_fd, (struct sockaddr *) &my_addr, NULL)) == -1) {
            LOGI("--client連接失敗--");
            continue;
        } else {
            /**
             * 下面這段應當交由子線程運行
             */
            LOGI("--client連接成功--");
            LOGI("--開始接收數據--");
            string msg = "";
            int readLen = 10;
            char buf[readLen + 1];
            int recvbytes = 0;
            for (recvbytes = recv(client_fd, buf, readLen, 0); ; recvbytes = recv(sock_fd, buf,
                                                                                  readLen, 0)) {
                if (recvbytes == -1) {
                    LOGI("--讀取數據失敗--");
                    break;
                } else {
                    buf[recvbytes] = '\0';
                    msg += buf;
                    if ('\r' == buf[recvbytes - 1] || '\n' == buf[recvbytes - 1]) break;
                    LOGI("--讀取到數據:%s", msg.data());
//                    if (recvbytes < readLen)break;
                }
            }
            LOGI("--最終讀取到的數據:%s", msg.data());
            if ("xcc\n" == msg) {
                msg = "--xcc--\n";
            } else msg = "--send-->" + msg;

            const char *buff = msg.data();
            int len = msg.length();
            if (send(client_fd, buff, len, 0) == -1) {
                LOGI("--發送數據失敗--");
            } else
                LOGI("--發送數據成功--");

            LOGI("--關閉client_socket連接--");
            close(client_fd);
        }
    }
    LOGI("--關閉socket連接--");
//5.關閉socket,需要頭文件unistd.h
    /**
     * 注,此處的關閉,只是關閉服務socket,也就是關閉綁定監聽等。
     * 但是client_socket是不會因此關閉
     */
    close(sock_fd);
    serve_sock_fd = 0;
}

2.關閉服務socket

void Java_com_xcc_app5_TCPUtils_stopServe(JNIEnv * env, jclass
jcl){
LOGI("--stopServe--");
isServe = 0;
/**
 * 注:這樣操作的本意在close(socket);時,能中斷accept()操作,
 * 但是accept()是堵塞式操作,沒法中斷。
 * 未能百度到解決方案,但別人建議使用select來監聽
 */
if(serve_sock_fd)close(serve_sock_fd);
serve_sock_fd = 0;
}

3.客戶端連接服務socket

void connectServe() {
    LOGI("--connectServe--");
    int sock_fd;//記錄socket
    //1.創建socket
    /**
     * PF_INET ipv4協議
     * SOCK_STREAM 表示使用TCP協議
     * SOCK_DGRAM 表示使用UDP協議
    */
    if (-1 == (sock_fd = socket(PF_INET, SOCK_STREAM, 0))) {
        LOGI("--socket創建出錯--");
    } else
        LOGI("--socket創建成功--");


    //2.連接ip地址
    /**
     * 通過域名獲取地址可使用gethostbyname
     * inet_addr將ip地址字符串轉成網絡字節序IP
     */
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = PF_INET;//設置協議
    serv_addr.sin_port = htons(SERVPORT);//設置端口
    serv_addr.sin_addr.s_addr = inet_addr("192.168.1.164");//設置ip地址,需要頭文件arpa/inet.h
    bzero(&(serv_addr.sin_zero), 8);
    if (-1 == connect(sock_fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr))) {
        LOGI("--socket連接出錯--");
    } else
        LOGI("--socket連接成功--");
    //3.發送數據
    /**
     *  sockfd:指定發送端套接字描述符。
     *  buff:存放要發送數據的緩衝區
     *  nbytes:實際要改善的數據的字節數(實際發送的長度)
     *  flags:一般設置爲0
     */
    string text = "磁磁帥\n";
    const char *buff = text.data();
    int len = text.length();
    if (send(sock_fd, buff, len, 0) == -1) {
        LOGI("--發送數據失敗--");
    } else
        LOGI("--發送數據成功--");
    //4.接收數據
    char buf[MAXDATASIZE + 1];
    int recvbytes = 0;
    /**
*  sockfd:套接字描述符。
*  buf:數據的緩衝區
*  nbytes:緩衝大小
*  flags:一般設置爲0
*/
//    這是我原先讀取數據的操作
//    if ((recvbytes = recv(sock_fd, buf, MAXDATASIZE, 0)) == -1) {
//        LOGI("--讀取數據失敗--");
//    } else {
//        LOGI("--讀取數據成功--");
//        buf[recvbytes] = '\0';
//        LOGI("--讀取到的數據:%s",buf);
//    }
    //當數據較多,緩衝區就裝不下,讀取方式就要改成如下
    string msg = "";
    for (recvbytes = recv(sock_fd, buf, MAXDATASIZE, 0); ; recvbytes = recv(sock_fd, buf,
                                                                            MAXDATASIZE, 0)) {
        if (recvbytes == -1) {
            LOGI("--讀取數據失敗--");
            break;
        } else {
            buf[recvbytes] = '\0';
            msg += buf;
            if (recvbytes < MAXDATASIZE) {
                break;
            }
        }
    }
    LOGI("--最終讀取到的數據:%s", msg.data());

    LOGI("--斷開socket連接--");
//5.關閉socket,需要頭文件unistd.h
    close(sock_fd);
}


學習時編寫的完整代碼,下載地址:
碼雲:http://git.oschina.net/rookieci/NDKStudy
github:https://github.com/cookieci/NDKStudy


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章