我的NDK学习开发笔记(五)
2017-06-08 22:03
232 查看
本地Socket编程
在Linux中创建socket时,不填写ip地址。然后填写命名,客户端访问时同样填写命名。这种socket就叫本地socket。因为方式和TCPSocket很像,我便在之前的代码上修改,来学习测试。
创建使用与TCP不同的地方:
1.创建本地socket时,需要使用AF_UNIX协议,否则出现绑定失败。如:sock_fd = socket(AF_UNIX, SOCK_STREAM,0)
2.连接socket不需要端口号和ip地址。
3.连接及绑定地址使用sockaddr_un ,如:structsockaddr_unserv_addr;//注意,此处是sockaddr_un,不是sockaddr_in
详细代码如下:
服务端:
客户端:
最终总结:由于边工作边学习,在花了一段时间后终于学习完了。本以为会花掉更多的时间,因为没有学到用C写Activity,所以时间较短。界面绘制及多媒体的C编程需要用到OpenGL或是SDL,需要学习的还有很多。对于java系编程的我,学用C编写多媒体应用还是很困难的,只能先等我掌握好已学的NDK再说了。
学习时编写的完整代码,下载地址:
码云:http://git.oschina.net/rookieci/NDKStudy
github:https://github.com/cookieci/NDKStudy
在Linux中创建socket时,不填写ip地址。然后填写命名,客户端访问时同样填写命名。这种socket就叫本地socket。因为方式和TCPSocket很像,我便在之前的代码上修改,来学习测试。
创建使用与TCP不同的地方:
1.创建本地socket时,需要使用AF_UNIX协议,否则出现绑定失败。如:sock_fd = socket(AF_UNIX, SOCK_STREAM,0)
2.连接socket不需要端口号和ip地址。
3.连接及绑定地址使用sockaddr_un ,如:structsockaddr_unserv_addr;//注意,此处是sockaddr_un,不是sockaddr_in
详细代码如下:
服务端:
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))) { //在绑定本地socket时,需要使用AF_UNIX,否则出现绑定失败 if (-1 == (sock_fd = socket(AF_UNIX, 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只能本地访问 //若创建本地socket,使用一下代码 const char *name = "@xccsocket";//此处抽象命名,用@站位 /** * 本地socket有两种命名方式 * 一、文件命名:socket会根据此命名创建一个同名的socket文件,客户端连接的时候通过读取该socket文件连接到socket服务端。 * 这种方式的弊端是服务端必须对socket文件的路径具备写权限,客户端必须知道socket文件路径,且必须对该路径有读权限。 * 二、抽象命名,这种方式不需要创建socket文件,只需要命名一个全局名字,即可让客户端根据此名字进行连接。 *注:抽象命名在对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0,即sun_path[0] = 0 */ unlink(name);//删除原先的socket对象 struct sockaddr_un my_addr; my_addr.sun_family = AF_UNIX;//设置协议 strcpy(my_addr.sun_path, name); my_addr.sun_path[0]=0;//抽象命名需要 //const size_t nameLen = strlen(name);//文件命名只这样使用 const size_t nameLen = strlen(name) + offsetof(struct sockaddr_un, sun_path); if (bind(sock_fd, (struct sockaddr *) &my_addr, nameLen) == -1){//这是绑定本地socket使用 // if (bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {//绑定网络socket使用 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); //注意,此处是sockaddr_un,不是sockaddr_in socklen_t sin_size = sizeof(struct sockaddr_un); 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; }
客户端:
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))) { //创建本地socket时,需要使用AF_UNIX,否则出现绑定失败 if (-1 == (sock_fd = socket(AF_UNIX, 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); //若创建本地socket,使用一下代码 const char *name = "@xccsocket";//此处抽象命名,用@站位 /** * 本地socket有两种命名方式 * 一、文件命名:socket会根据此命名创建一个同名的socket文件,客户端连接的时候通过读取该socket文件连接到socket服务端。 * 这种方式的弊端是服务端必须对socket文件的路径具备写权限,客户端必须知道socket文件路径,且必须对该路径有读权限。 * 二、抽象命名,这种方式不需要创建socket文件,只需要命名一个全局名字,即可让客户端根据此名字进行连接。 *注:抽象命名在对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0,即sun_path[0] = 0 */ struct sockaddr_un serv_addr;//注意,此处是sockaddr_un,不是sockaddr_in serv_addr.sun_family = AF_UNIX;//设置协议 strcpy(serv_addr.sun_path, name); serv_addr.sun_pa 4000 th[0]=0;//抽象命名需要 //const size_t nameLen = strlen(name);//文件命名只这样使用 const size_t nameLen = strlen(name) + offsetof(struct sockaddr_un, sun_path); if (-1 == connect(sock_fd, (struct sockaddr *) &serv_addr, nameLen)) {//这是连接本地socket使用 // if (-1 == connect(sock_fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr))) {//这是连接网络socket使用 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); }
最终总结:由于边工作边学习,在花了一段时间后终于学习完了。本以为会花掉更多的时间,因为没有学到用C写Activity,所以时间较短。界面绘制及多媒体的C编程需要用到OpenGL或是SDL,需要学习的还有很多。对于java系编程的我,学用C编写多媒体应用还是很困难的,只能先等我掌握好已学的NDK再说了。
学习时编写的完整代码,下载地址:
码云:http://git.oschina.net/rookieci/NDKStudy
github:https://github.com/cookieci/NDKStudy
相关文章推荐
- Android开发学习笔记(11):NDK与Cygwin配置手顺
- NDK开发学习笔记—C/C++访问java成员
- Android Native 代码NDK开发学习笔记
- NDK开发学习笔记
- Android NDK(学习笔记四)—— 在NDK开发中JNI打印Log信息
- NDK开发学习笔记(2):JNI访问Java中的方法
- 我的NDK学习开发笔记(一)
- NDK开发笔记(二)---JNI的学习
- Android(java)学习笔记259:JNI之NDK开发步骤
- 我的NDK学习开发笔记(四)
- NDK开发学习笔记(1):JNI开发步骤及遇到的问题详解
- 我的NDK学习开发笔记(二)
- NDK开发学习笔记—C代码返回中文乱码处理
- Android Native 代码NDK开发学习笔记
- Android开发学习笔记(10):NDK安装手顺及应用
- NDK开发笔记(二)---JNI的学习
- NDK开发学习笔记(3):JNI访问数组、引用、异常处理、缓存策略
- 《Web Service 编程 --用C#.NET 开发网络服务》北京希望出版社 我的学习笔记(第二章)(也就是书上抄了一写东西而已)
- 用S60操作系统SDK开发NOKIA手机应用程序-学习笔记(3)
- Java软件开发学习笔记(四)