TCP/UDP/LOCAL Socket C++的实现
来源:互联网 发布:java json传值到前台 编辑:程序博客网 时间:2024/06/07 11:11
前言
本篇文章主要是记录了Native层Socket的实现,包括TCP、UDP和LOCAL。一般来说,我们使用Socket通信都是通过调用Java层的Api来实现,而Java层Api最终也会走到Native层来具体实现,而且其实不管是http、tcp、udp还是bluetooth抑或是WifiDirect都是在Socket的基础上封装而来,只不过协议不同而已。虽然在开发中通过调用Java层Api可以很方便的使用Socket,但是一些特殊情况下还是得用底层Socket的实现,比如需要写一些安全性较高的网络连接的时候,就需要自己在Native层实现网络的请求。
实现
// JNI#include <jni.h>// NULL#include <stdio.h>// va_list, vsnprintf#include <stdarg.h>// errno#include <errno.h>// strerror_r, memset#include <string.h>// socket, bind, getsockname, listen, accept, recv, send, connect#include <sys/types.h>#include <sys/socket.h>// sockaddr_un#include <sys/un.h>// htons, sockaddr_in#include <netinet/in.h>// inet_ntop#include <arpa/inet.h>// close, unlink#include <unistd.h>// offsetof#include <stddef.h>// Max log message length#define MAX_LOG_MESSAGE_LENGTH 256// Max data buffer size#define MAX_BUFFER_SIZE 80/** * Logs the given message to the application. * * @param env JNIEnv interface. * @param obj object instance. * @param format message format and arguments. */static void LogMessage( JNIEnv *env, jobject obj, const char *format, ...) { // Cached log method ID static jmethodID methodID = NULL; // If method ID is not cached if (NULL == methodID) { // Get class from object jclass clazz = env->GetObjectClass(obj); // Get the method ID for the given method methodID = env->GetMethodID(clazz, "logMessage", "(Ljava/lang/String;)V"); // Release the class reference env->DeleteLocalRef(clazz); } // If method is found if (NULL != methodID) { // Format the log message char buffer[MAX_LOG_MESSAGE_LENGTH]; va_list ap; va_start(ap, format); vsnprintf(buffer, MAX_LOG_MESSAGE_LENGTH, format, ap); va_end(ap); // Convert the buffer to a Java string jstring message = env->NewStringUTF(buffer); // If string is properly constructed if (NULL != message) { // Log message env->CallVoidMethod(obj, methodID, message); // Release the message reference env->DeleteLocalRef(message); } }}/** * Throws a new exception using the given exception class * and exception message. * * @param env JNIEnv interface. * @param className class name. * @param message exception message. */static void ThrowException( JNIEnv *env, const char *className, const char *message) { // Get the exception class jclass clazz = env->FindClass(className); // If exception class is found if (NULL != clazz) { // Throw exception env->ThrowNew(clazz, message); // Release local class reference env->DeleteLocalRef(clazz); }}/** * Throws a new exception using the given exception class * and error message based on the error number. * * @param env JNIEnv interface. * @param className class name. * @param errnum error number. */static void ThrowErrnoException( JNIEnv *env, const char *className, int errnum) { char buffer[MAX_LOG_MESSAGE_LENGTH]; // Get message for the error number if (-1 == strerror_r(errnum, buffer, MAX_LOG_MESSAGE_LENGTH)) { strerror_r(errno, buffer, MAX_LOG_MESSAGE_LENGTH); } // Throw exception ThrowException(env, className, buffer);}/** * Constructs a new TCP socket. * * @param env JNIEnv interface. * @param obj object instance. * @return socket descriptor. * @throws IOException */static int NewTcpSocket(JNIEnv *env, jobject obj) { // Construct socket LogMessage(env, obj, "Constructing a new TCP socket..."); int tcpSocket = socket(PF_INET, SOCK_STREAM, 0); // Check if socket is properly constructed if (-1 == tcpSocket) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } return tcpSocket;}/** * Binds socket to a port number. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param port port number or zero for random port. * @throws IOException */static void BindSocketToPort( JNIEnv *env, jobject obj, int sd, unsigned short port) { struct sockaddr_in address; // Address to bind socket memset(&address, 0, sizeof(address)); address.sin_family = PF_INET; // Bind to all addresses address.sin_addr.s_addr = htonl(INADDR_ANY); // Convert port to network byte order address.sin_port = htons(port); // Bind socket LogMessage(env, obj, "Binding to port %hu.", port); if (-1 == bind(sd, (struct sockaddr *) &address, sizeof(address))) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); }}/** * Gets the port number socket is currently binded. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @return port number. * @throws IOException */static unsigned short GetSocketPort( JNIEnv *env, jobject obj, int sd) { unsigned short port = 0; struct sockaddr_in address; socklen_t addressLength = sizeof(address); // Get the socket address if (-1 == getsockname(sd, (struct sockaddr *) &address, &addressLength)) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // Convert port to host byte order port = ntohs(address.sin_port); LogMessage(env, obj, "Binded to random port %hu.", port); } return port;}/** * Listens on given socket with the given backlog for * pending connections. When the backlog is full, the * new connections will be rejected. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param backlog backlog size. * @throws IOException */static void ListenOnSocket( JNIEnv *env, jobject obj, int sd, int backlog) { // Listen on socket with the given backlog LogMessage(env, obj, "Listening on socket with a backlog of %d pending connections.", backlog); if (-1 == listen(sd, backlog)) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); }}/** * Logs the IP address and the port number from the * given address. * * @param env JNIEnv interface. * @param obj object instance. * @param message message text. * @param address adress instance. * @throws IOException */static void LogAddress( JNIEnv *env, jobject obj, const char *message, const struct sockaddr_in *address) { char ip[INET_ADDRSTRLEN]; // Convert the IP address to string if (NULL == inet_ntop(PF_INET, &(address->sin_addr), ip, INET_ADDRSTRLEN)) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // Convert port to host byte order unsigned short port = ntohs(address->sin_port); // Log address LogMessage(env, obj, "%s %s:%hu.", message, ip, port); }}/** * Blocks and waits for incoming client connections on the * given socket. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @return client socket. * @throws IOException */static int AcceptOnSocket( JNIEnv *env, jobject obj, int sd) { struct sockaddr_in address; socklen_t addressLength = sizeof(address); // Blocks and waits for an incoming client connection // and accepts it LogMessage(env, obj, "Waiting for a client connection..."); int clientSocket = accept(sd, (struct sockaddr *) &address, &addressLength); // If client socket is not valid if (-1 == clientSocket) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // Log address LogAddress(env, obj, "Client connection from ", &address); } return clientSocket;}/** * Block and receive data from the socket into the buffer. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param buffer data buffer. * @param bufferSize buffer size. * @return receive size. * @throws IOException */static ssize_t ReceiveFromSocket( JNIEnv *env, jobject obj, int sd, char *buffer, size_t bufferSize) { // Block and receive data from the socket into the buffer LogMessage(env, obj, "Receiving from the socket..."); ssize_t recvSize = recv(sd, buffer, bufferSize - 1, 0); // If receive is failed if (-1 == recvSize) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // NULL terminate the buffer to make it a string buffer[recvSize] = NULL; // If data is received if (recvSize > 0) { LogMessage(env, obj, "Received %d bytes: %s", recvSize, buffer); } else { LogMessage(env, obj, "Client disconnected."); } } return recvSize;}/** * Send data buffer to the socket. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param buffer data buffer. * @param bufferSize buffer size. * @return sent size. * @throws IOException */static ssize_t SendToSocket( JNIEnv *env, jobject obj, int sd, const char *buffer, size_t bufferSize) { // Send data buffer to the socket LogMessage(env, obj, "Sending to the socket..."); ssize_t sentSize = send(sd, buffer, bufferSize, 0); // If send is failed if (-1 == sentSize) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { if (sentSize > 0) { LogMessage(env, obj, "Sent %d bytes: %s", sentSize, buffer); } else { LogMessage(env, obj, "Client disconnected."); } } return sentSize;}/** * Connects to given IP address and given port. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param ip IP address. * @param port port number. * @throws IOException */static void ConnectToAddress( JNIEnv *env, jobject obj, int sd, const char *ip, unsigned short port) { // Connecting to given IP address and given port LogMessage(env, obj, "Connecting to %s:%uh...", ip, port); struct sockaddr_in address; memset(&address, 0, sizeof(address)); address.sin_family = PF_INET; // Convert IP address string to Internet address if (0 == inet_aton(ip, &(address.sin_addr))) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // Convert port to network byte order address.sin_port = htons(port); // Connect to address if (-1 == connect(sd, (const sockaddr *) &address, sizeof(address))) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { LogMessage(env, obj, "Connected."); } }}void Java_com_apress_echo_EchoClientActivity_nativeStartTcpClient( JNIEnv *env, jobject obj, jstring ip, jint port, jstring message) { // Construct a new TCP socket. int clientSocket = NewTcpSocket(env, obj); if (NULL == env->ExceptionOccurred()) { // Get IP address as C string const char *ipAddress = env->GetStringUTFChars(ip, NULL); if (NULL == ipAddress) goto exit; // Connect to IP address and port ConnectToAddress(env, obj, clientSocket, ipAddress, (unsigned short) port); // Release the IP address env->ReleaseStringUTFChars(ip, ipAddress); // If connection was successful if (NULL != env->ExceptionOccurred()) goto exit; // Get message as C string const char *messageText = env->GetStringUTFChars(message, NULL); if (NULL == messageText) goto exit; // Get the message size jsize messageSize = env->GetStringUTFLength(message); // Send message to socket SendToSocket(env, obj, clientSocket, messageText, messageSize); // Release the message text env->ReleaseStringUTFChars(message, messageText); // If send was not successful if (NULL != env->ExceptionOccurred()) goto exit; char buffer[MAX_BUFFER_SIZE]; // Receive from the socket ReceiveFromSocket(env, obj, clientSocket, buffer, MAX_BUFFER_SIZE); } exit: if (clientSocket > 0) { close(clientSocket); }}void Java_com_apress_echo_EchoServerActivity_nativeStartTcpServer( JNIEnv *env, jobject obj, jint port) { // Construct a new TCP socket. int serverSocket = NewTcpSocket(env, obj); if (NULL == env->ExceptionOccurred()) { // Bind socket to a port number BindSocketToPort(env, obj, serverSocket, (unsigned short) port); if (NULL != env->ExceptionOccurred()) goto exit; // If random port number is requested if (0 == port) { // Get the port number socket is currently binded GetSocketPort(env, obj, serverSocket); if (NULL != env->ExceptionOccurred()) goto exit; } // Listen on socket with a backlog of 4 pending connections ListenOnSocket(env, obj, serverSocket, 4); if (NULL != env->ExceptionOccurred()) goto exit; // Accept a client connection on socket int clientSocket = AcceptOnSocket(env, obj, serverSocket); if (NULL != env->ExceptionOccurred()) goto exit; char buffer[MAX_BUFFER_SIZE]; ssize_t recvSize; ssize_t sentSize; // Receive and send back the data while (1) { // Receive from the socket recvSize = ReceiveFromSocket(env, obj, clientSocket, buffer, MAX_BUFFER_SIZE); if ((0 == recvSize) || (NULL != env->ExceptionOccurred())) break; // Send to the socket sentSize = SendToSocket(env, obj, clientSocket, buffer, (size_t) recvSize); if ((0 == sentSize) || (NULL != env->ExceptionOccurred())) break; } // Close the client socket close(clientSocket); } exit: if (serverSocket > 0) { close(serverSocket); }}/** * Constructs a new UDP socket. * * @param env JNIEnv interface. * @param obj object instance. * @return socket descriptor. * @throws IOException */static int NewUdpSocket(JNIEnv *env, jobject obj) { // Construct socket LogMessage(env, obj, "Constructing a new UDP socket..."); int udpSocket = socket(PF_INET, SOCK_DGRAM, 0); // Check if socket is properly constructed if (-1 == udpSocket) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } return udpSocket;}/** * Block and receive datagram from the socket into * the buffer, and populate the client address. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param address client address. * @param buffer data buffer. * @param bufferSize buffer size. * @return receive size. * @throws IOException */static ssize_t ReceiveDatagramFromSocket( JNIEnv *env, jobject obj, int sd, struct sockaddr_in *address, char *buffer, size_t bufferSize) { socklen_t addressLength = sizeof(struct sockaddr_in); // Receive datagram from socket LogMessage(env, obj, "Receiving from the socket..."); ssize_t recvSize = recvfrom(sd, buffer, bufferSize, 0, (struct sockaddr *) address, &addressLength); // If receive is failed if (-1 == recvSize) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else { // Log address LogAddress(env, obj, "Received from", address); // NULL terminate the buffer to make it a string buffer[recvSize] = NULL; // If data is received if (recvSize > 0) { LogMessage(env, obj, "Received %d bytes: %s", recvSize, buffer); } } return recvSize;}/** * Sends datagram to the given address using the given socket. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param address remote address. * @param buffer data buffer. * @param bufferSize buffer size. * @return sent size. * @throws IOException */static ssize_t SendDatagramToSocket( JNIEnv *env, jobject obj, int sd, const struct sockaddr_in *address, const char *buffer, size_t bufferSize) { // Send data buffer to the socket LogAddress(env, obj, "Sending to", address); ssize_t sentSize = sendto(sd, buffer, bufferSize, 0, (const sockaddr *) address, sizeof(struct sockaddr_in)); // If send is failed if (-1 == sentSize) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } else if (sentSize > 0) { LogMessage(env, obj, "Sent %d bytes: %s", sentSize, buffer); } return sentSize;}void Java_com_apress_echo_EchoClientActivity_nativeStartUdpClient( JNIEnv *env, jobject obj, jstring ip, jint port, jstring message) { // Construct a new UDP socket. int clientSocket = NewUdpSocket(env, obj); if (NULL == env->ExceptionOccurred()) { struct sockaddr_in address; memset(&address, 0, sizeof(address)); address.sin_family = PF_INET; // Get IP address as C string const char *ipAddress = env->GetStringUTFChars(ip, NULL); if (NULL == ipAddress) goto exit; // Convert IP address string to Internet address int result = inet_aton(ipAddress, &(address.sin_addr)); // Release the IP address env->ReleaseStringUTFChars(ip, ipAddress); // If conversion is failed if (0 == result) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); goto exit; } // Convert port to network byte order address.sin_port = htons(port); // Get message as C string const char *messageText = env->GetStringUTFChars(message, NULL); if (NULL == messageText) goto exit; // Get the message size jsize messageSize = env->GetStringUTFLength(message); // Send message to socket SendDatagramToSocket(env, obj, clientSocket, &address, messageText, messageSize); // Release the message text env->ReleaseStringUTFChars(message, messageText); // If send was not successful if (NULL != env->ExceptionOccurred()) goto exit; char buffer[MAX_BUFFER_SIZE]; // Clear address memset(&address, 0, sizeof(address)); // Receive from the socket ReceiveDatagramFromSocket(env, obj, clientSocket, &address, buffer, MAX_BUFFER_SIZE); } exit: if (clientSocket > 0) { close(clientSocket); }}void Java_com_apress_echo_EchoServerActivity_nativeStartUdpServer( JNIEnv *env, jobject obj, jint port) { // Construct a new UDP socket. int serverSocket = NewUdpSocket(env, obj); if (NULL == env->ExceptionOccurred()) { // Bind socket to a port number BindSocketToPort(env, obj, serverSocket, (unsigned short) port); if (NULL != env->ExceptionOccurred()) goto exit; // If random port number is requested if (0 == port) { // Get the port number socket is currently binded GetSocketPort(env, obj, serverSocket); if (NULL != env->ExceptionOccurred()) goto exit; } // Client address struct sockaddr_in address; memset(&address, 0, sizeof(address)); char buffer[MAX_BUFFER_SIZE]; ssize_t recvSize; ssize_t sentSize; // Receive from the socket recvSize = ReceiveDatagramFromSocket(env, obj, serverSocket, &address, buffer, MAX_BUFFER_SIZE); if ((0 == recvSize) || (NULL != env->ExceptionOccurred())) goto exit; // Send to the socket sentSize = SendDatagramToSocket(env, obj, serverSocket, &address, buffer, (size_t) recvSize); } exit: if (serverSocket > 0) { close(serverSocket); }}/** * Constructs a new Local UNIX socket. * * @param env JNIEnv interface. * @param obj object instance. * @return socket descriptor. * @throws IOException */static int NewLocalSocket(JNIEnv *env, jobject obj) { // Construct socket LogMessage(env, obj, "Constructing a new Local UNIX socket..."); int localSocket = socket(PF_LOCAL, SOCK_STREAM, 0); // Check if socket is properly constructed if (-1 == localSocket) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } return localSocket;}/** * Binds a local UNIX socket to a name. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @param name socket name. * @throws IOException */static void BindLocalSocketToName( JNIEnv *env, jobject obj, int sd, const char *name) { struct sockaddr_un address; // Name length const size_t nameLength = strlen(name); // Path length is initiall equal to name length size_t pathLength = nameLength; // If name is not starting with a slash it is // in the abstract namespace bool abstractNamespace = ('/' != name[0]); // Abstract namespace requires having the first // byte of the path to be the zero byte, update // the path length to include the zero byte if (abstractNamespace) { pathLength++; } // Check the path length if (pathLength > sizeof(address.sun_path)) { // Throw an exception with error number ThrowException(env, "java/io/IOException", "Name is too big."); } else { // Clear the address bytes memset(&address, 0, sizeof(address)); address.sun_family = PF_LOCAL; // Socket path char *sunPath = address.sun_path; // First byte must be zero to use the abstract namespace if (abstractNamespace) { *sunPath++ = NULL; } // Append the local name strcpy(sunPath, name); // Address length socklen_t addressLength = (offsetof(struct sockaddr_un, sun_path)) + pathLength; // Unlink if the socket name is already binded unlink(address.sun_path); // Bind socket LogMessage(env, obj, "Binding to local name %s%s.", (abstractNamespace) ? "(null)" : "", name); if (-1 == bind(sd, (struct sockaddr *) &address, addressLength)) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } }}/** * Blocks and waits for incoming client connections on the * given socket. * * @param env JNIEnv interface. * @param obj object instance. * @param sd socket descriptor. * @return client socket. * @throws IOException */static int AcceptOnLocalSocket( JNIEnv *env, jobject obj, int sd) { // Blocks and waits for an incoming client connection // and accepts it LogMessage(env, obj, "Waiting for a client connection..."); int clientSocket = accept(sd, NULL, NULL); // If client socket is not valid if (-1 == clientSocket) { // Throw an exception with error number ThrowErrnoException(env, "java/io/IOException", errno); } return clientSocket;}void Java_com_apress_echo_LocalEchoActivity_nativeStartLocalServer( JNIEnv *env, jobject obj, jstring name) { // Construct a new local UNIX socket. int serverSocket = NewLocalSocket(env, obj); if (NULL == env->ExceptionOccurred()) { // Get name as C string const char *nameText = env->GetStringUTFChars(name, NULL); if (NULL == nameText) goto exit; // Bind socket to a port number BindLocalSocketToName(env, obj, serverSocket, nameText); // Release the name text env->ReleaseStringUTFChars(name, nameText); // If bind is failed if (NULL != env->ExceptionOccurred()) goto exit; // Listen on socket with a backlog of 4 pending connections ListenOnSocket(env, obj, serverSocket, 4); if (NULL != env->ExceptionOccurred()) goto exit; // Accept a client connection on socket int clientSocket = AcceptOnLocalSocket(env, obj, serverSocket); if (NULL != env->ExceptionOccurred()) goto exit; char buffer[MAX_BUFFER_SIZE]; ssize_t recvSize; ssize_t sentSize; // Receive and send back the data while (1) { // Receive from the socket recvSize = ReceiveFromSocket(env, obj, clientSocket, buffer, MAX_BUFFER_SIZE); if ((0 == recvSize) || (NULL != env->ExceptionOccurred())) break; // Send to the socket sentSize = SendToSocket(env, obj, clientSocket, buffer, (size_t) recvSize); if ((0 == sentSize) || (NULL != env->ExceptionOccurred())) break; } // Close the client socket close(clientSocket); } exit: if (serverSocket > 0) { close(serverSocket); }}
完整代码传送门:https://github.com/qq542391099/NativeSocket
阅读全文
0 0
- TCP/UDP/LOCAL Socket C++的实现
- windows下C调用socket实现TCP和UDP通信
- 基于 Socket 的 UDP 和 TCP 编程介绍 C语言实现
- linux下C/C++网络编程基本:socket实现tcp和udp的例子
- C语言的Socket编程例子(TCP和UDP)
- C语言的Socket编程例子(TCP和UDP)
- C语言的Socket编程例子(TCP和UDP)
- C语言的Socket编程例子(TCP和UDP)
- C语言的Socket编程例子(TCP和UDP)
- C语言的Socket编程例子(TCP和UDP)
- TCP与UDP的简单发送与实现(socket)
- TCP/Ip协议+UDP协议+Socket通信的实现
- python socket实现简单的(TCP/UDP)服务器/客户端
- JAVA socket实现TCP/UDP的多线程通信
- socket/TCP/UDP基础及Unity聊天室的实现
- 基于TCP和UDP的socket通信(C++实现)
- Socket之TCP与UDP的编程实现
- Socket编程(TCP/UDP) - 初学(C语言)
- Maven 项目中常用命令
- Android控件之Checkbox和RadioButton
- [深度探索C++对象模型]memcpy和memset注意事项
- 自动 上传到github 自动新建 推送 自动脚本
- 【总结】扩展欧几里得算法
- TCP/UDP/LOCAL Socket C++的实现
- MySQL安装 Apply security settings报错1045解决方案
- Spring配置数据源的几种形式
- day4_字符串挖取
- POJ1681画家问题(枚举)采用位运算
- 剑指offer_数组中的重复数字
- Mac 安装Tomcat
- Python code 提取UML
- 设计模式思维导图