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