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, (
struct
sockaddr
*) &my_addr,
sizeof
(
struct
sockaddr
)),参数:socket标记,绑定地址结构体(包含ip地址和端口号),绑定地址数据长度。
3.开始监听。创建
服务socket需要的步骤。
listen(sock_fd,
BACKLOG
),参数:socket标记,最大同时连接请求数。
4.等待连接。创建
服务socket需要的步骤。
accept(sock_fd, (
struct
sockaddr
*) &my_addr, &sin_size)),参数:socket标记,监听地址结构体,监听地址数据长度。返回:客户socket连接。注:在close(socket)时,并不会中断该操作。建议使用select来监听
5.连接ip地址。创建
客户端socket需要的步骤。
connect(sock_fd, (
struct
sockaddr
*) &serv_addr,
sizeof
(
struct
sockaddr
))。
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.需要引入的文件
#i
nclude
<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