POSIX Socket API

Socket 通信分为三种:

1、面向连接的通信TCP;

2、无连接的通信UDP;

3、本地通信Local

将 Android Emulator 上的 <port number> 与主机上的 <port number> 映射。任何输入到主机上端口号所指定端口的连接都会通过 adb 转发到 Android Emulator 的指定端口上。

adb -s emulator-5556 forward tcp:57102 tcp:57102

 本地 socket 族既支持基于流的 socket 协议,也支持基于数据报的 socket 协议。

AbstractEchoActivity.java

  1 package com.apress.echo;
  2 
  3 import android.app.Activity;
  4 import android.os.Bundle;
  5 import android.os.Handler;
  6 import android.view.View;
  7 import android.view.View.OnClickListener;
  8 import android.widget.Button;
  9 import android.widget.EditText;
 10 import android.widget.ScrollView;
 11 import android.widget.TextView;
 12 
 13 /**
 14  * Abstract echo activity object.
 15  * 
 16  * @author Onur Cinar
 17  */
 18 public abstract class AbstractEchoActivity extends Activity implements  OnClickListener {
 19     /** Port number. 端口号 */
 20     protected EditText portEdit;
 21 
 22     /** Server button. 服务按钮 */
 23     protected Button startButton;
 24 
 25     /** Log scroll. 日志滚动 */
 26     protected ScrollView logScroll;
 27 
 28     /** Log view. 日志视图 */
 29     protected TextView logView;
 30 
 31     /** Layout ID. 布局ID */
 32     private final int layoutID;
 33 
 34     /**
 35      * Constructor. 构造函数
 36      * 
 37      * @param layoutID
 38      *            layout ID.
 39      */
 40     public AbstractEchoActivity(int layoutID) {
 41         // 绑定用户界面组件
 42         this.layoutID = layoutID;
 43     }
 44 
 45     public void onCreate(Bundle savedInstanceState) {
 46         super.onCreate(savedInstanceState);
 47         setContentView(layoutID);
 48 
 49         portEdit = (EditText) findViewById(R.id.port_edit);
 50         startButton = (Button) findViewById(R.id.start_button);
 51         logScroll = (ScrollView) findViewById(R.id.log_scroll);
 52         logView = (TextView) findViewById(R.id.log_view);
 53 
 54         startButton.setOnClickListener(this);
 55     }
 56 
 57     public void onClick(View view) {
 58         if (view == startButton) {
 59             onStartButtonClicked();
 60         }
 61     }
 62 
 63     /**
 64      * On start button clicked.
 65      */
 66     protected abstract void onStartButtonClicked();
 67 
 68     /**
 69      * Gets the port number as an integer.以整型获取端口号
 70      * 
 71      * @return port number or null.
 72      */
 73     protected Integer getPort() {
 74         Integer port;
 75 
 76         try {
 77             port = Integer.valueOf(portEdit.getText().toString());
 78         } catch (NumberFormatException e) {
 79             port = null;
 80         }
 81 
 82         return port;
 83     }
 84 
 85     /**
 86      * Logs the given message. 记录给定的消息
 87      * 
 88      * @param message
 89      *            log message. 日志消息
 90      */
 91     protected void logMessage(final String message) {
 92         runOnUiThread(new Runnable() {
 93             public void run() {
 94                 logMessageDirect(message);
 95             }
 96         });
 97     }
 98 
 99     /**
100      * Logs given message directly. 直接记录给定的消息
101      * 
102      * @param message
103      *            log message. 日志消息
104      */
105     protected void logMessageDirect(final String message) {
106         logView.append(message);
107         logView.append("\n");
108         logScroll.fullScroll(View.FOCUS_DOWN);
109     }
110     
111     /**
112      * Abstract async echo task. 抽象异步 echo 任务
113      * 应用在一个单独的线程而不是UI线程中执行网络操作
114      */
115     protected abstract class AbstractEchoTask extends Thread {
116         /** Handler object. 对象 */
117         private final Handler handler;
118         
119         /**
120          * Constructor. 构造函数
121          */
122         public AbstractEchoTask() {
123             handler = new Handler();
124         }
125         
126         /**
127          * On pre execute callback in calling thread. 
128          * 在调用线程中先执行回调
129          */
130         protected void onPreExecute() {
131             startButton.setEnabled(false);
132             logView.setText("");
133         }
134 
135         public synchronized void start() {
136             onPreExecute();            
137             super.start();
138         }
139 
140         public void run() {
141             onBackground();
142             
143             handler.post(new Runnable() {
144                 public void run() {
145                     onPostExecute();
146                 }
147             });
148         }
149 
150         /**
151          * On background callback in new thread.
152          * 在新线程中的背景回调
153          */
154         protected abstract void onBackground();
155 
156         /**
157          * On post execute callback in calling thread.
158          * 在调用线程中后执行回调
159          */
160         protected void onPostExecute() {
161             startButton.setEnabled(true);
162         }
163     }
164 
165     static {
166         System.loadLibrary("Echo");
167     }
168 }
AbstractEchoActivity

EchoClientActivity.java

  1 package com.apress.echo;
  2 
  3 import android.os.Bundle;
  4 import android.widget.EditText;
  5 
  6 /**
  7  * Echo client.
  8  * 
  9  * @author Onur Cinar
 10  */
 11 public class EchoClientActivity extends AbstractEchoActivity {    
 12     /** IP address. */
 13     private EditText ipEdit;
 14 
 15     /** Message edit. 消息编辑 */
 16     private EditText messageEdit;
 17 
 18     /**
 19      * Constructor. 构造函数
 20      */
 21     public EchoClientActivity() {
 22         super(R.layout.activity_echo_client);
 23     }
 24 
 25     public void onCreate(Bundle savedInstanceState) {
 26         super.onCreate(savedInstanceState);
 27 
 28         ipEdit = (EditText) findViewById(R.id.ip_edit);
 29         messageEdit = (EditText) findViewById(R.id.message_edit);
 30     }
 31 
 32     protected void onStartButtonClicked() {
 33         String ip = ipEdit.getText().toString();
 34         Integer port = getPort();
 35         String message = messageEdit.getText().toString();
 36 
 37         if ((0 != ip.length()) && (port != null) && (0 != message.length())) {
 38             ClientTask clientTask = new ClientTask(ip, port, message);
 39             clientTask.start();
 40         }
 41     }
 42 
 43     /**
 44      * Starts the TCP client with the given server IP address and port number,
 45      * and sends the given message.
 46      * 根据给定服务器IP 地址和端口号启动TCP客户端,并且发送给定消息
 47      * 
 48      * @param ip
 49      *            IP address.
 50      * @param port
 51      *            port number.
 52      * @param message
 53      *            message text.
 54      * @throws Exception
 55      */
 56     private native void nativeStartTcpClient(String ip, int port, String message)
 57             throws Exception;
 58 
 59     /**
 60      * Starts the UDP client with the given server IP address and port number.
 61      * 
 62      * @param ip
 63      *            IP address.
 64      * @param port
 65      *            port number.
 66      * @param message
 67      *            message text.
 68      * @throws Exception
 69      */
 70     private native void nativeStartUdpClient(String ip, int port, String message)
 71             throws Exception;
 72 
 73     /**
 74      * Client task. 客户端任务
 75      */
 76     private class ClientTask extends AbstractEchoTask {
 77         /** IP address to connect. 连接的IP地址 */
 78         private final String ip;
 79 
 80         /** Port number. 端口号 */
 81         private final int port;
 82 
 83         /** Message text to send. 发送的消息文本 */
 84         private final String message;
 85 
 86         /**
 87          * Constructor. 构造函数
 88          * 
 89          * @param ip
 90          *            IP address to connect.
 91          * @param port
 92          *            port number to connect.
 93          * @param message
 94          *            message text to send.
 95          */
 96         public ClientTask(String ip, int port, String message) {
 97             this.ip = ip;
 98             this.port = port;
 99             this.message = message;
100         }
101 
102         protected void onBackground() {
103             logMessage("Starting client.");
104 
105             try {
106                 nativeStartTcpClient(ip, port, message);
107                 //nativeStartUdpClient(ip, port, message);
108             } catch (Throwable e) {
109                 logMessage(e.getMessage());
110             }
111 
112             logMessage("Client terminated.");
113         }
114     }
115 }
EchoClientActivity

EchoServerActivity .java

 1 package com.apress.echo;
 2 
 3 /**
 4  * Echo server.
 5  * 
 6  * @author Onur Cinar
 7  */
 8 public class EchoServerActivity extends AbstractEchoActivity {
 9     /**
10      * Constructor. 构造函数
11      */
12     public EchoServerActivity() {
13         super(R.layout.activity_echo_server);
14     }
15 
16     protected void onStartButtonClicked() {
17         Integer port = getPort();
18         if (port != null) {
19             ServerTask serverTask = new ServerTask(port);
20             serverTask.start();
21         }
22     }
23 
24     /**
25      * Starts the TCP server on the given port.
26      * 根据给定端口启动 TCP 服务
27      * 
28      * @param port
29      * 端口号          port number.
30      * @throws Exception
31      */
32     private native void nativeStartTcpServer(int port) throws Exception;
33 
34     /**
35      * Starts the UDP server on the given port.
36      * 根据给定端口启动 UDP 服务
37      * 
38      * @param port
39      * 端口号           port number.
40      * @throws Exception
41      */
42     private native void nativeStartUdpServer(int port) throws Exception;
43 
44     /**
45      * Server task. 服务端任务
46      */
47     private class ServerTask extends AbstractEchoTask {
48         /** 端口号  Port number. */
49         private final int port;
50         
51         /**
52          * Constructor.构造函数
53          * 
54          * @param port
55          * 端口号           port number.
56          */
57         public ServerTask(int port) {
58             this.port = port;
59         }
60 
61         protected void onBackground() {
62             logMessage("Starting server.");
63 
64             try {
65                 nativeStartTcpServer(port);
66                 //nativeStartUdpServer(port);
67             } catch (Exception e) {
68                 logMessage(e.getMessage());
69             }
70 
71             logMessage("Server terminated.");
72         }
73     }
74 }
EchoServerActivity

LocalEchoActivity.java

  1 package com.apress.echo;
  2 
  3 import java.io.File;
  4 import java.io.InputStream;
  5 import java.io.OutputStream;
  6 
  7 import android.net.LocalSocket;
  8 import android.net.LocalSocketAddress;
  9 import android.os.Bundle;
 10 import android.widget.EditText;
 11 
 12 /**
 13  * Echo local socket server and client.
 14  * Echo 本地 socket 服务器和客户端
 15  * 
 16  * @author Onur Cinar
 17  */
 18 public class LocalEchoActivity extends AbstractEchoActivity {
 19     /** Message edit. 消息编辑 */
 20     private EditText messageEdit;
 21 
 22     /**
 23      * Constructor. 构造函数
 24      */
 25     public LocalEchoActivity() {
 26         super(R.layout.activity_local_echo);
 27     }
 28 
 29     public void onCreate(Bundle savedInstanceState) {
 30         super.onCreate(savedInstanceState);
 31 
 32         messageEdit = (EditText) findViewById(R.id.message_edit);
 33     }
 34 
 35     protected void onStartButtonClicked() {
 36         String name = portEdit.getText().toString();
 37         String message = messageEdit.getText().toString();
 38 
 39         if ((name.length() > 0) && (message.length() > 0)) {
 40             String socketName;
 41 
 42             // If it is a filesystem socket, prepend the application files directory
 43             // 如果是 filesystem socket,预先准备应用程序的文件目录
 44             if (isFilesystemSocket(name)) {
 45                 File file = new File(getFilesDir(), name);
 46                 socketName = file.getAbsolutePath();
 47             } else {
 48                 socketName = name;
 49             }
 50 
 51             ServerTask serverTask = new ServerTask(socketName);
 52             serverTask.start();
 53 
 54             ClientTask clientTask = new ClientTask(socketName, message);
 55             clientTask.start();
 56         }
 57     }
 58 
 59     /**
 60      * Check if name is a filesystem socket.
 61      * 检查名称是否是 filesystem socket
 62      * 
 63      * @param name
 64      *            socket name.
 65      * @return filesystem socket.
 66      */
 67     private boolean isFilesystemSocket(String name) {
 68         return name.startsWith("/");
 69     }
 70 
 71     /**
 72      * Starts the Local UNIX socket server binded to given name.
 73      * 启动绑定到给定名称的本地 UNIX socket 服务器
 74      * 
 75      * @param name
 76      *            socket name.
 77      * @throws Exception
 78      */
 79     private native void nativeStartLocalServer(String name) throws Exception;
 80 
 81     /**
 82      * Starts the local UNIX socket client.
 83      * 启动本地 UNXI socket 客户端
 84      * 
 85      * @param port
 86      *            port number.
 87      * @param message
 88      *            message text.
 89      * @throws Exception
 90      */
 91     private void startLocalClient(String name, String message) throws Exception {
 92         // Construct a local socket 构造一个本地 socket
 93         LocalSocket clientSocket = new LocalSocket();
 94         try {
 95             // Set the socket namespace 设置 socket 名称空间
 96             LocalSocketAddress.Namespace namespace;
 97             if (isFilesystemSocket(name)) {
 98                 namespace = LocalSocketAddress.Namespace.FILESYSTEM;
 99             } else {
100                 namespace = LocalSocketAddress.Namespace.ABSTRACT;
101             }
102     
103             // Construct local socket address 构造本地 socket 地址
104             LocalSocketAddress address = new LocalSocketAddress(name, namespace);
105     
106             // Connect to local socket 连接到本地 socket
107             logMessage("Connecting to " + name);        
108             clientSocket.connect(address);
109             logMessage("Connected.");
110             
111             // Get message as bytes 以字节形式获取消息
112             byte[] messageBytes = message.getBytes();
113             
114             // Send message bytes to the socket 发送消息字节到 socket 
115             logMessage("Sending to the socket...");
116             OutputStream outputStream = clientSocket.getOutputStream();
117             outputStream.write(messageBytes);
118             logMessage(String.format("Sent %d bytes: %s", messageBytes.length, message));
119                         
120             // Receive the message back from the socket 从 socket 中接收消息返回
121             logMessage("Receiving from the socket...");
122             InputStream inputStream = clientSocket.getInputStream();
123             int readSize = inputStream.read(messageBytes);
124             
125             String receivedMessage = new String(messageBytes, 0, readSize);
126             logMessage(String.format("Received %d bytes: %s", readSize, receivedMessage));
127             
128             // Close streams 关闭流
129             outputStream.close();
130             inputStream.close();
131             
132         } finally { 
133             // Close the local socket 关闭本地 socket
134             clientSocket.close();
135         }
136     }
137 
138     /**
139      * Server task. 服务器任务
140      */
141     private class ServerTask extends AbstractEchoTask {
142         /** Socket name. */
143         private final String name;
144 
145         /**
146          * Constructor. 构造函数
147          * 
148          * @param name
149          *            socket name.
150          */
151         public ServerTask(String name) {
152             this.name = name;
153         }
154 
155         protected void onBackground() {
156             logMessage("Starting server.");
157 
158             try {
159                 nativeStartLocalServer(name);
160             } catch (Exception e) {
161                 logMessage(e.getMessage());
162             }
163 
164             logMessage("Server terminated.");
165         }
166     }
167 
168     /**
169      * Client task. 客户端任务
170      */
171     private class ClientTask extends Thread {
172         /** Socket name. */
173         private final String name;
174 
175         /** Message text to send. 发送的消息文本 */
176         private final String message;
177 
178         /**
179          * Constructor. 构造函数
180          * 
181          * @parma name socket name.
182          * @param message
183          *            message text to send.
184          */
185         public ClientTask(String name, String message) {
186             this.name = name;
187             this.message = message;
188         }
189 
190         public void run() {
191             logMessage("Starting client.");
192 
193             try {
194                 startLocalClient(name, message);
195             } catch (Exception e) {
196                 logMessage(e.getMessage());
197             }
198 
199             logMessage("Client terminated.");
200         }
201     }
202 }
LocalEchoActivity

原生代码 Echo.cpp

   1 #include "com_apress_echo_EchoClientActivity.h"
   2 #include "com_apress_echo_EchoServerActivity.h"
   3 #include "com_apress_echo_LocalEchoActivity.h"
   4 
   5 // JNI
   6 #include <jni.h>
   7 
   8 // NULL
   9 #include <stdio.h>
  10 
  11 // va_list, vsnprintf
  12 #include <stdarg.h>
  13 
  14 // errno
  15 #include <errno.h>
  16 
  17 // strerror_r, memset
  18 #include <string.h>
  19 
  20 // socket, bind, getsockname, listen, accept, recv, send, connect
  21 #include <sys/types.h>
  22 #include <sys/socket.h>
  23 
  24 // sockaddr_un
  25 #include <sys/un.h>
  26 
  27 // htons, sockaddr_in
  28 #include <netinet/in.h>
  29 
  30 // inet_ntop
  31 #include <arpa/inet.h>
  32 
  33 // close, unlink
  34 #include <unistd.h>
  35 
  36 // offsetof
  37 #include <stddef.h>
  38 
  39 // Max log message length 最大日志消息长度
  40 #define MAX_LOG_MESSAGE_LENGTH 256
  41 
  42 // Max data buffer size 最大数据缓冲区大小
  43 #define MAX_BUFFER_SIZE 80
  44 
  45 /**
  46  * Logs the given message to the application.
  47  * 将给定的消息记录到应用程序
  48  *
  49  * @param env JNIEnv interface.
  50  * @param obj object instance.
  51  * @param format message format and arguments.
  52  */
  53 static void LogMessage(
  54         JNIEnv* env,
  55         jobject obj,
  56         const char* format,
  57         ...)
  58 {
  59     // Cached log method ID 缓存日志方法 ID
  60     static jmethodID methodID = NULL;
  61 
  62     // If method ID is not cached 如果方法ID未缓存
  63     if (NULL == methodID)
  64     {
  65         // Get class from object 从对象获取类
  66         jclass clazz = env->GetObjectClass(obj);
  67 
  68         // Get the method ID for the given method 从给定的方法中获取方法ID
  69         methodID = env->GetMethodID(clazz, "logMessage", "(Ljava/lang/String;)V");
  70 
  71         // Release the class reference 释放类引用
  72         env->DeleteLocalRef(clazz);
  73     }
  74 
  75     // If method is found 如果找到方法
  76     if (NULL != methodID)
  77     {
  78         // Format the log message 格式化日志消息
  79         char buffer[MAX_LOG_MESSAGE_LENGTH];
  80 
  81         va_list ap;
  82         va_start(ap, format);
  83         vsnprintf(buffer, MAX_LOG_MESSAGE_LENGTH, format, ap);
  84         va_end(ap);
  85 
  86         // Convert the buffer to a Java string 将缓冲区转换为 Java 字符串
  87         jstring message = env->NewStringUTF(buffer);
  88 
  89         // If string is properly constructed 如果字符串构造正确
  90         if (NULL != message)
  91         {
  92             // Log message 记录消息
  93             env->CallVoidMethod(obj, methodID, message);
  94 
  95             // Release the message reference 释放消息引用
  96             env->DeleteLocalRef(message);
  97         }
  98     }
  99 }
 100 
 101 /**
 102  * Throws a new exception using the given exception class
 103  * and exception message.
 104  * 用给定的异常类和异常消息抛出新的异常
 105  *
 106  * @param env JNIEnv interface.
 107  * @param className class name.
 108  * @param message exception message.
 109  */
 110 static void ThrowException(
 111         JNIEnv* env,
 112         const char* className,
 113         const char* message)
 114 {
 115     // Get the exception class 获取异常类
 116     jclass clazz = env->FindClass(className);
 117 
 118     // If exception class is found 如果异常类未找到
 119     if (NULL != clazz)
 120     {
 121         // Throw exception 抛出异常
 122         env->ThrowNew(clazz, message);
 123 
 124         // Release local class reference 释放原生类引用
 125         env->DeleteLocalRef(clazz);
 126     }
 127 }
 128 
 129 /**
 130  * Throws a new exception using the given exception class
 131  * and error message based on the error number.
 132  * 用给定异常类和基于错误号的错误消息抛出新异常
 133  *
 134  * @param env JNIEnv interface.
 135  * @param className class name.
 136  * @param errnum error number.
 137  */
 138 static void ThrowErrnoException(
 139         JNIEnv* env,
 140         const char* className,
 141         int errnum)
 142 {
 143     char buffer[MAX_LOG_MESSAGE_LENGTH];
 144 
 145     // Get message for the error number 获取错误号消息
 146     if (-1 == strerror_r(errnum, buffer, MAX_LOG_MESSAGE_LENGTH))
 147     {
 148         strerror_r(errno, buffer, MAX_LOG_MESSAGE_LENGTH);
 149     }
 150 
 151     // Throw exception 抛出异常
 152     ThrowException(env, className, buffer);
 153 }
 154 
 155 /**
 156  * Constructs a new TCP socket.
 157  * 构造新的 TCP socket
 158  *
 159  * @param env JNIEnv interface.
 160  * @param obj object instance.
 161  * @return socket descriptor.
 162  * @throws IOException
 163  */
 164 static int NewTcpSocket(JNIEnv* env, jobject obj)
 165 {
 166     // Construct 构造 socket
 167     LogMessage(env, obj, "Constructing a new TCP socket...");
 168     int tcpSocket = socket(PF_INET, SOCK_STREAM, 0);
 169 
 170     // Check if socket is properly constructed 检查 socket 构造是否正确
 171     if (-1 == tcpSocket)
 172     {
 173         // Throw an exception with error number 抛出带错误符号的异常
 174         ThrowErrnoException(env, "java/io/IOException", errno);
 175     }
 176 
 177     return tcpSocket;
 178 }
 179 
 180 /**
 181  * Binds socket to a port number.
 182  * 将 socket 绑定到某一端口号
 183  *
 184  * @param env JNIEnv interface.
 185  * @param obj object instance.
 186  * @param sd socket descriptor.
 187  * @param port port number or zero for random port.
 188  * @throws IOException
 189  */
 190 static void BindSocketToPort(
 191         JNIEnv* env,
 192         jobject obj,
 193         int sd,
 194         unsigned short port)
 195 {
 196     struct sockaddr_in address;
 197 
 198     // Address to bind socket 绑定 socket 地址
 199     memset(&address, 0, sizeof(address));
 200     address.sin_family = PF_INET;
 201 
 202     // Bind to all addresses 绑定到所有的 IP 地址
 203     address.sin_addr.s_addr = htonl(INADDR_ANY);
 204 
 205     // Convert port to network byte order 将端口转换为网络字节顺序
 206     address.sin_port = htons(port);
 207 
 208     // Bind 绑定 socket
 209     LogMessage(env, obj, "Binding to port %hu.", port);
 210     if (-1 == bind(sd, (struct sockaddr*) &address, sizeof(address)))
 211     {
 212         // Throw an exception with error number 抛出带错误号的异常
 213         ThrowErrnoException(env, "java/io/IOException", errno);
 214     }
 215 }
 216 
 217 /**
 218  * Gets the port number socket is currently binded.
 219  * 获取当前绑定的端口号 socket
 220  *
 221  * @param env JNIEnv interface.
 222  * @param obj object instance.
 223  * @param sd socket descriptor.
 224  * @return port number.
 225  * @throws IOException
 226  */
 227 static unsigned short GetSocketPort(
 228         JNIEnv* env,
 229         jobject obj,
 230         int sd)
 231 {
 232     unsigned short port = 0;
 233 
 234     struct sockaddr_in address;
 235     socklen_t addressLength = sizeof(address);
 236 
 237     // Get the socket address 获取 socket 地址
 238     if (-1 == getsockname(sd, (struct sockaddr*) &address, &addressLength))
 239     {
 240         // Throw an exception with error number 抛出带错误号的异常
 241         ThrowErrnoException(env, "java/io/IOException", errno);
 242     }
 243     else
 244     {
 245         // Convert port to host byte order 将端口转换为主机字节顺序
 246         port = ntohs(address.sin_port);
 247 
 248         LogMessage(env, obj, "Binded to random port %hu.", port);
 249     }
 250 
 251     return port;
 252 }
 253 
 254 /**
 255  * Listens on given socket with the given backlog for
 256  * pending connections. When the backlog is full, the
 257  * new connections will be rejected.
 258  * 监听指定的待处理连接的 backlog 的 socket,当 backlog 已满时拒绝新的连接
 259  *
 260  * @param env JNIEnv interface.
 261  * @param obj object instance.
 262  * @param sd socket descriptor.
 263  * @param backlog backlog size.
 264  * @throws IOException
 265  */
 266 static void ListenOnSocket(
 267         JNIEnv* env,
 268         jobject obj,
 269         int sd,
 270         int backlog)
 271 {
 272     // Listen on socket with the given backlog 监听给定 backlog 的 socket
 273     LogMessage(env, obj,
 274             "Listening on socket with a backlog of %d pending connections.",
 275             backlog);
 276 
 277     if (-1 == listen(sd, backlog))
 278     {
 279         // Throw an exception with error number 抛出带错误符号的异常
 280         ThrowErrnoException(env, "java/io/IOException", errno);
 281     }
 282 }
 283 
 284 /**
 285  * Logs the IP address and the port number from the given address.
 286  * 记录给定地址的 IP 地址和端口号
 287  *
 288  * @param env JNIEnv interface.
 289  * @param obj object instance.
 290  * @param message message text.
 291  * @param address adress instance.
 292  * @throws IOException
 293  */
 294 static void LogAddress(
 295         JNIEnv* env,
 296         jobject obj,
 297         const char* message,
 298         const struct sockaddr_in* address)
 299 {
 300     char ip[INET_ADDRSTRLEN];
 301 
 302     // Convert the IP address to string 将 IP 地址转换为字符串
 303     if (NULL == inet_ntop(PF_INET,
 304             &(address->sin_addr),
 305             ip,
 306             INET_ADDRSTRLEN))
 307     {
 308         // Throw an exception with error number 抛出带错误符号的异常
 309         ThrowErrnoException(env, "java/io/IOException", errno);
 310     }
 311     else
 312     {
 313         // Convert port to host byte order 将端口转换为主机字节顺序
 314         unsigned short port = ntohs(address->sin_port);
 315 
 316         // Log address 记录地址
 317         LogMessage(env, obj, "%s %s:%hu.", message, ip, port);
 318     }
 319 }
 320 
 321 /**
 322  * Blocks and waits for incoming client connections on the given socket.
 323  * 在给定的 socket 上阻塞和等待进来的客户连接
 324  *
 325  * @param env JNIEnv interface.
 326  * @param obj object instance.
 327  * @param sd socket descriptor.
 328  * @return client socket.
 329  * @throws IOException
 330  */
 331 static int AcceptOnSocket(
 332         JNIEnv* env,
 333         jobject obj,
 334         int sd)
 335 {
 336     struct sockaddr_in address;
 337     socklen_t addressLength = sizeof(address);
 338 
 339     // Blocks and waits for an incoming client connection and accepts it
 340     // 阻塞和等待进来的客户连接并且接受它
 341     LogMessage(env, obj, "Waiting for a client connection...");
 342 
 343     int clientSocket = accept(sd,
 344             (struct sockaddr*) &address,
 345             &addressLength);
 346 
 347     // If client socket is not valid 如果客户 socket 无效
 348     if (-1 == clientSocket)
 349     {
 350         // Throw an exception with error number 抛出带错误符号的异常
 351         ThrowErrnoException(env, "java/io/IOException", errno);
 352     }
 353     else
 354     {
 355         // Log address 记录地址
 356         LogAddress(env, obj, "Client connection from ", &address);
 357     }
 358 
 359     return clientSocket;
 360 }
 361 
 362 /**
 363  * Block and receive data from the socket into the buffer.
 364  * 阻塞并接收来自 socket 的数据放到缓冲区
 365  *
 366  * @param env JNIEnv interface.
 367  * @param obj object instance.
 368  * @param sd socket descriptor.
 369  * @param buffer data buffer.
 370  * @param bufferSize buffer size.
 371  * @return receive size.
 372  * @throws IOException
 373  */
 374 static ssize_t ReceiveFromSocket(
 375         JNIEnv* env,
 376         jobject obj,
 377         int sd,
 378         char* buffer,
 379         size_t bufferSize)
 380 {
 381     // Block and receive data from the socket into the buffer
 382     // 阻塞并接收来自 socket 的数据放到缓冲区
 383     LogMessage(env, obj, "Receiving from the socket...");
 384     ssize_t recvSize = recv(sd, buffer, bufferSize - 1, 0);
 385 
 386     // If receive is failed 如果接收失败
 387     if (-1 == recvSize)
 388     {
 389         // Throw an exception with error number 抛出带错误号的异常
 390         ThrowErrnoException(env, "java/io/IOException", errno);
 391     }
 392     else
 393     {
 394         // NULL terminate the buffer to make it a string 以空结尾缓冲区形成一个字符串
 395         buffer[recvSize] = NULL;
 396 
 397         // If data is received 如果数据接收成功
 398         if (recvSize > 0)
 399         {
 400             LogMessage(env, obj, "Received %d bytes: %s", recvSize, buffer);
 401         }
 402         else
 403         {
 404             LogMessage(env, obj, "Client disconnected.");
 405         }
 406     }
 407 
 408     return recvSize;
 409 }
 410 
 411 /**
 412  * Send data buffer to the socket.
 413  * 将数据缓冲区发送到 socket
 414  *
 415  * @param env JNIEnv interface.
 416  * @param obj object instance.
 417  * @param sd socket descriptor.
 418  * @param buffer data buffer.
 419  * @param bufferSize buffer size.
 420  * @return sent size.
 421  * @throws IOException
 422  */
 423 static ssize_t SendToSocket(
 424         JNIEnv* env,
 425         jobject obj,
 426         int sd,
 427         const char* buffer,
 428         size_t bufferSize)
 429 {
 430     // Send data buffer to the socket 将数据缓冲区发送到 socket
 431     LogMessage(env, obj, "Sending to the socket...");
 432     ssize_t sentSize = send(sd, buffer, bufferSize, 0);
 433 
 434     // If send is failed 如果发送失败
 435     if (-1 == sentSize)
 436     {
 437         // Throw an exception with error number 抛出带错误符号的异常
 438         ThrowErrnoException(env, "java/io/IOException", errno);
 439     }
 440     else
 441     {
 442         if (sentSize > 0)
 443         {
 444             LogMessage(env, obj, "Sent %d bytes: %s", sentSize, buffer);
 445         }
 446         else
 447         {
 448             LogMessage(env, obj, "Client disconnected.");
 449         }
 450     }
 451 
 452     return sentSize;
 453 }
 454 
 455 /**
 456  * Connects to given IP address and given port.
 457  * 连接到给定的 IP 地址和给定的端口号
 458  *
 459  * @param env JNIEnv interface.
 460  * @param obj object instance.
 461  * @param sd socket descriptor.
 462  * @param ip IP address.
 463  * @param port port number.
 464  * @throws IOException
 465  */
 466 static void ConnectToAddress(
 467         JNIEnv* env,
 468         jobject obj,
 469         int sd,
 470         const char* ip,
 471         unsigned short port)
 472 {
 473     // Connecting to given IP address and given port 连接到给定的 IP 地址和给定的端口号
 474     LogMessage(env, obj, "Connecting to %s:%uh...", ip, port);
 475 
 476     struct sockaddr_in address;
 477 
 478     memset(&address, 0, sizeof(address));
 479     address.sin_family = PF_INET;
 480 
 481     // Convert IP address string to Internet address 将 IP 地址字符串转换为网络地址
 482     if (0 == inet_aton(ip, &(address.sin_addr)))
 483     {
 484         // Throw an exception with error number 抛出带错误号的异常
 485         ThrowErrnoException(env, "java/io/IOException", errno);
 486     }
 487     else
 488     {
 489         // Convert port to network byte order 将端口号转换为网络字节顺序
 490         address.sin_port = htons(port);
 491 
 492         // Connect to address 转换为地址
 493         if (-1 == connect(sd, (const sockaddr*) &address, sizeof(address)))
 494         {
 495             // Throw an exception with error number 抛出带错误号的异常
 496             ThrowErrnoException(env, "java/io/IOException", errno);
 497         }
 498         else
 499         {
 500             LogMessage(env, obj, "Connected.");
 501         }
 502     }
 503 }
 504 
 505 // 原生 TCP 客户端
 506 void Java_com_apress_echo_EchoClientActivity_nativeStartTcpClient(
 507         JNIEnv* env,
 508         jobject obj,
 509         jstring ip,
 510         jint port,
 511         jstring message)
 512 {
 513     // Construct a new TCP socket. 构造新的 TCP socket
 514     int clientSocket = NewTcpSocket(env, obj);
 515     if (NULL == env->ExceptionOccurred())
 516     {
 517         // Get IP address as C string 以 C 字符串形式获取 IP 地址
 518         const char* ipAddress = env->GetStringUTFChars(ip, NULL);
 519         if (NULL == ipAddress)
 520             goto exit;
 521 
 522         // Connect to IP address and port 连接到 IP 地址和端口
 523         ConnectToAddress(env, obj, clientSocket, ipAddress,    (unsigned short) port);
 524 
 525         // Release the IP address 释放 IP 地址
 526         env->ReleaseStringUTFChars(ip, ipAddress);
 527 
 528         // If connection was successful 如果连接成功
 529         if (NULL != env->ExceptionOccurred())
 530             goto exit;
 531 
 532         // Get message as C string 以 C 字符串形式获取消息
 533         const char* messageText = env->GetStringUTFChars(message, NULL);
 534         if (NULL == messageText)
 535             goto exit;
 536 
 537         // Get the message size 获取消息大小
 538         jsize messageSize = env->GetStringUTFLength(message);
 539 
 540         // Send message to socket 发送消息给 socket
 541         SendToSocket(env, obj, clientSocket, messageText, messageSize);
 542 
 543         // Release the message text 释放消息文本
 544         env->ReleaseStringUTFChars(message, messageText);
 545 
 546         // If send was not successful 如果发送未成功
 547         if (NULL != env->ExceptionOccurred())
 548             goto exit;
 549 
 550         char buffer[MAX_BUFFER_SIZE];
 551 
 552         // Receive from the socket 从 socket 接收
 553         ReceiveFromSocket(env, obj, clientSocket, buffer, MAX_BUFFER_SIZE);
 554     }
 555 
 556 exit:
 557     if (clientSocket > 0)
 558     {
 559         close(clientSocket);
 560     }
 561 }
 562 
 563 // 原生 TCP 服务器方法
 564 void Java_com_apress_echo_EchoServerActivity_nativeStartTcpServer(
 565         JNIEnv* env,
 566         jobject obj,
 567         jint port)
 568 {
 569     // Construct a new TCP socket. 构造新的 TCP socket
 570     int serverSocket = NewTcpSocket(env, obj);
 571     if (NULL == env->ExceptionOccurred())
 572     {
 573         // Bind socket to a port number 将 socket 绑定到某端口号
 574         BindSocketToPort(env, obj, serverSocket, (unsigned short) port);
 575         if (NULL != env->ExceptionOccurred())
 576             goto exit;
 577 
 578         // If random port number is requested 如果请求了随机端口号
 579         if (0 == port)
 580         {
 581             // Get the port number socket is currently binded 获取当前绑定的端口号 socket
 582             GetSocketPort(env, obj, serverSocket);
 583             if (NULL != env->ExceptionOccurred())
 584                 goto exit;
 585         }
 586 
 587         // Listen on socket with a backlog of 4 pending connections 监听有4 个等待连接的 backlog 的 socket
 588         ListenOnSocket(env, obj, serverSocket, 4);
 589         if (NULL != env->ExceptionOccurred())
 590             goto exit;
 591 
 592         // Accept a client connection on socket 接受 socket 的一个客户连接
 593         int clientSocket = AcceptOnSocket(env, obj, serverSocket);
 594         if (NULL != env->ExceptionOccurred())
 595             goto exit;
 596 
 597         char buffer[MAX_BUFFER_SIZE];
 598         ssize_t recvSize;
 599         ssize_t sentSize;
 600 
 601         // Receive and send back the data 接收并发送回数据
 602         while (1)
 603         {
 604             // Receive from the socket 从 socket 中接收
 605             recvSize = ReceiveFromSocket(env, obj, clientSocket,
 606                     buffer, MAX_BUFFER_SIZE);
 607 
 608             if ((0 == recvSize) || (NULL != env->ExceptionOccurred()))
 609                 break;
 610 
 611             // Send to the socket 发送给 socket
 612             sentSize = SendToSocket(env, obj, clientSocket,
 613                     buffer, (size_t) recvSize);
 614 
 615             if ((0 == sentSize) || (NULL != env->ExceptionOccurred()))
 616                 break;
 617         }
 618 
 619         // Close the client socket 关闭客户端 socket
 620         close(clientSocket);
 621     }
 622 
 623 exit:
 624     if (serverSocket > 0)
 625     {
 626         close(serverSocket);
 627     }
 628 }
 629 
 630 /**
 631  * Constructs a new UDP socket.
 632  * 构造新的 UDP socket
 633  *
 634  * @param env JNIEnv interface.
 635  * @param obj object instance.
 636  * @return socket descriptor.
 637  * @throws IOException
 638  */
 639 static int NewUdpSocket(JNIEnv* env, jobject obj)
 640 {
 641     // Construct socket 构造 socket
 642     LogMessage(env, obj, "Constructing a new UDP socket...");
 643     int udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
 644 
 645     // Check if socket is properly constructed
 646     // 检查 socket 构造是否正确
 647     if (-1 == udpSocket)
 648     {
 649         // Throw an exception with error number 抛出带错误号的异常
 650         ThrowErrnoException(env, "java/io/IOException", errno);
 651     }
 652 
 653     return udpSocket;
 654 }
 655 
 656 /**
 657  * Block and receive datagram from the socket into
 658  * the buffer, and populate the client address.
 659  * 从 socket 中阻塞并接收数据报保存到缓冲区,填充客户端地址
 660  *
 661  * @param env JNIEnv interface.
 662  * @param obj object instance.
 663  * @param sd socket descriptor.
 664  * @param address client address.
 665  * @param buffer data buffer.
 666  * @param bufferSize buffer size.
 667  * @return receive size.
 668  * @throws IOException
 669  */
 670 static ssize_t ReceiveDatagramFromSocket(
 671         JNIEnv* env,
 672         jobject obj,
 673         int sd,
 674         struct sockaddr_in* address,
 675         char* buffer,
 676         size_t bufferSize)
 677 {
 678     socklen_t addressLength = sizeof(struct sockaddr_in);
 679 
 680     // Receive datagram from socket 从 socket 中接收数据报
 681     LogMessage(env, obj, "Receiving from the socket...");
 682     ssize_t recvSize = recvfrom(sd, buffer, bufferSize, 0,
 683             (struct sockaddr*) address,    &addressLength);
 684 
 685     // If receive is failed 如果接收失败
 686     if (-1 == recvSize)
 687     {
 688         // Throw an exception with error number 抛出带错误号的异常
 689         ThrowErrnoException(env, "java/io/IOException", errno);
 690     }
 691     else
 692     {
 693         // Log address 记录地址
 694         LogAddress(env, obj, "Received from", address);
 695 
 696         // NULL terminate the buffer to make it a string
 697         // 以空终止缓冲区使其为一个字符串
 698         buffer[recvSize] = NULL;
 699 
 700         // If data is received 如果数据已经接收
 701         if (recvSize > 0)
 702         {
 703             LogMessage(env, obj, "Received %d bytes: %s", recvSize, buffer);
 704         }
 705     }
 706 
 707     return recvSize;
 708 }
 709 
 710 /**
 711  * Sends datagram to the given address using the given socket.
 712  * 用给定的 socket 发送数据到给定的地址
 713  *
 714  * @param env JNIEnv interface.
 715  * @param obj object instance.
 716  * @param sd socket descriptor.
 717  * @param address remote address.
 718  * @param buffer data buffer.
 719  * @param bufferSize buffer size.
 720  * @return sent size.
 721  * @throws IOException
 722  */
 723 static ssize_t SendDatagramToSocket(
 724         JNIEnv* env,
 725         jobject obj,
 726         int sd,
 727         const struct sockaddr_in* address,
 728         const char* buffer,
 729         size_t bufferSize)
 730 {
 731     // Send data buffer to the socket 向 socket 发送数据缓冲区
 732     LogAddress(env, obj, "Sending to", address);
 733     ssize_t sentSize = sendto(sd, buffer, bufferSize, 0,
 734             (const sockaddr*) address,    sizeof(struct sockaddr_in));
 735 
 736     // If send is failed 如果发送失败
 737     if (-1 == sentSize)
 738     {
 739         // Throw an exception with error number 抛出带错误号的异常
 740         ThrowErrnoException(env, "java/io/IOException", errno);
 741     }
 742     else if (sentSize > 0)
 743     {
 744         LogMessage(env, obj, "Sent %d bytes: %s", sentSize, buffer);
 745     }
 746 
 747     return sentSize;
 748 }
 749 
 750 void Java_com_apress_echo_EchoClientActivity_nativeStartUdpClient(
 751         JNIEnv* env,
 752         jobject obj,
 753         jstring ip,
 754         jint port,
 755         jstring message)
 756 {
 757     // Construct a new UDP socket. 构造一个新的 UDP socket
 758     int clientSocket = NewUdpSocket(env, obj);
 759     if (NULL == env->ExceptionOccurred())
 760     {
 761         struct sockaddr_in address;
 762 
 763         memset(&address, 0, sizeof(address));
 764         address.sin_family = PF_INET;
 765 
 766         // Get IP address as C string 以 C 字符串形式获取 IP 地址
 767         const char* ipAddress = env->GetStringUTFChars(ip, NULL);
 768         if (NULL == ipAddress)
 769             goto exit;
 770 
 771         // Convert IP address string to Internet address
 772         // 将 IP 地址字符串转换为网络地址
 773         int result = inet_aton(ipAddress, &(address.sin_addr));
 774 
 775         // Release the IP address 释放 IP 地址
 776         env->ReleaseStringUTFChars(ip, ipAddress);
 777 
 778         // If conversion is failed 如果转换失败
 779         if (0 == result)
 780         {
 781             // Throw an exception with error number 抛出带错误符号的异常
 782             ThrowErrnoException(env, "java/io/IOException", errno);
 783             goto exit;
 784         }
 785 
 786         // Convert port to network byte order 将端口转换为网络字节顺序
 787         address.sin_port = htons(port);
 788 
 789         // Get message as C string 以 C 字符串形式获取消息
 790         const char* messageText = env->GetStringUTFChars(message, NULL);
 791         if (NULL == messageText)
 792             goto exit;
 793 
 794         // Get the message size 获取消息大小
 795         jsize messageSize = env->GetStringUTFLength(message);
 796 
 797         // Send message to socket 发送消息给 socket
 798         SendDatagramToSocket(env, obj, clientSocket, &address,    messageText, messageSize);
 799 
 800         // Release the message text 释放消息文本
 801         env->ReleaseStringUTFChars(message, messageText);
 802 
 803         // If send was not successful 如果发送未成功
 804         if (NULL != env->ExceptionOccurred())
 805             goto exit;
 806 
 807         char buffer[MAX_BUFFER_SIZE];
 808 
 809         // Clear address 清除地址
 810         memset(&address, 0, sizeof(address));
 811 
 812         // Receive from the socket 从 socket 接收
 813         ReceiveDatagramFromSocket(env, obj, clientSocket, &address,    buffer, MAX_BUFFER_SIZE);
 814     }
 815 
 816 exit:
 817     if (clientSocket > 0)
 818     {
 819         close(clientSocket);
 820     }
 821 }
 822 
 823 void Java_com_apress_echo_EchoServerActivity_nativeStartUdpServer(
 824         JNIEnv* env,
 825         jobject obj,
 826         jint port)
 827 {
 828     // Construct a new UDP socket. 构造一个新的 UDP socket
 829     int serverSocket = NewUdpSocket(env, obj);
 830     if (NULL == env->ExceptionOccurred())
 831     {
 832         // Bind socket to a port number 将 socket 绑定到某一端口号
 833         BindSocketToPort(env, obj, serverSocket, (unsigned short) port);
 834         if (NULL != env->ExceptionOccurred())
 835             goto exit;
 836 
 837         // If random port number is requested 如果请求随机端口号
 838         if (0 == port)
 839         {
 840             // Get the port number socket is currently binded 获取当前绑定的端口号 socket
 841             GetSocketPort(env, obj, serverSocket);
 842             if (NULL != env->ExceptionOccurred())
 843                 goto exit;
 844         }
 845 
 846         // Client address 客户端地址
 847         struct sockaddr_in address;
 848         memset(&address, 0, sizeof(address));
 849 
 850         char buffer[MAX_BUFFER_SIZE];
 851         ssize_t recvSize;
 852         ssize_t sentSize;
 853 
 854         // Receive from the socket 从 socket 中接收
 855         recvSize = ReceiveDatagramFromSocket(env, obj, serverSocket, &address, buffer, MAX_BUFFER_SIZE);
 856 
 857         if ((0 == recvSize) || (NULL != env->ExceptionOccurred()))
 858             goto exit;
 859 
 860         // Send to the socket 发送给 socket
 861         sentSize = SendDatagramToSocket(env, obj, serverSocket,    &address, buffer, (size_t) recvSize);
 862     }
 863 
 864 exit:
 865     if (serverSocket > 0)
 866     {
 867         close(serverSocket);
 868     }
 869 }
 870 
 871 /**
 872  * Constructs a new Local UNIX socket.
 873  * 构造一个新的原生 UNIX socket
 874  *
 875  * @param env JNIEnv interface.
 876  * @param obj object instance.
 877  * @return socket descriptor.
 878  * @throws IOException
 879  */
 880 static int NewLocalSocket(JNIEnv* env, jobject obj)
 881 {
 882     // Construct socket 构造 socket
 883     LogMessage(env, obj, "Constructing a new Local UNIX socket...");
 884     int localSocket = socket(PF_LOCAL, SOCK_STREAM, 0);
 885 
 886     // Check if socket is properly constructed
 887     // 检查 socket 构造是否正确
 888     if (-1 == localSocket)
 889     {
 890         // Throw an exception with error number 抛出带错误号的异常
 891         ThrowErrnoException(env, "java/io/IOException", errno);
 892     }
 893 
 894     return localSocket;
 895 }
 896 
 897 /**
 898  * Binds a local UNIX socket to a name.
 899  * 将本地 UNIX socket 与某一名称绑定
 900  *
 901  * @param env JNIEnv interface.
 902  * @param obj object instance.
 903  * @param sd socket descriptor.
 904  * @param name socket name.
 905  * @throws IOException
 906  */
 907 static void BindLocalSocketToName(
 908         JNIEnv* env,
 909         jobject obj,
 910         int sd,
 911         const char* name)
 912 {
 913     struct sockaddr_un address;
 914 
 915     // Name length 名称长度
 916     const size_t nameLength = strlen(name);
 917 
 918     // Path length is initiall equal to name length
 919     // 路径长度初始化与名称长度相等
 920     size_t pathLength = nameLength;
 921 
 922     // If name is not starting with a slash it is in the abstract namespace
 923     // 如果名称不是以 '/' 开头,即它在抽象命名空间里
 924     bool abstractNamespace = ('/' != name[0]);
 925 
 926     // Abstract namespace requires having the first byte of the path to be the zero byte,
 927     //  update the path length to include the zero byte
 928     // 抽象命名空间要求目录的第一个字节是 0 字节,更新路径长度包括 0 字节。
 929     if (abstractNamespace)
 930     {
 931         pathLength++;
 932     }
 933 
 934     // Check the path length 检查路径长度
 935     if (pathLength > sizeof(address.sun_path))
 936     {
 937         // Throw an exception with error number 抛出带错误号的异常
 938         ThrowException(env, "java/io/IOException", "Name is too big.");
 939     }
 940     else
 941     {
 942         // Clear the address bytes 清除地址字节
 943         memset(&address, 0, sizeof(address));
 944         address.sun_family = PF_LOCAL;
 945 
 946         // Socket path
 947         char* sunPath = address.sun_path;
 948 
 949         // First byte must be zero to use the abstract namespace
 950         // 第一字节必须是 0 以使用抽象命名空间
 951         if (abstractNamespace)
 952         {
 953             *sunPath++ = NULL;
 954         }
 955 
 956         // Append the local name 追加本地名字
 957         strcpy(sunPath, name);
 958 
 959         // Address length 地址长度
 960         socklen_t addressLength = (offsetof(struct sockaddr_un, sun_path)) + pathLength;
 961 
 962         // Unlink if the socket name is already binded
 963         // 如果 socket 名称已绑定,取消连接
 964         unlink(address.sun_path);
 965 
 966         // Bind socket
 967         LogMessage(env, obj, "Binding to local name %s%s.",
 968                 (abstractNamespace) ? "(null)" : "", name);
 969 
 970         if (-1 == bind(sd, (struct sockaddr*) &address, addressLength))
 971         {
 972             // Throw an exception with error number 抛出带错误号的异常
 973             ThrowErrnoException(env, "java/io/IOException", errno);
 974         }
 975     }
 976 }
 977 
 978 /**
 979  * Blocks and waits for incoming client connections on the given socket.
 980  * 阻塞并等待给定 socket 上即将到来的客户端连接
 981  *
 982  * @param env JNIEnv interface.
 983  * @param obj object instance.
 984  * @param sd socket descriptor.
 985  * @return client socket.
 986  * @throws IOException
 987  */
 988 static int AcceptOnLocalSocket(
 989         JNIEnv* env,
 990         jobject obj,
 991         int sd)
 992 {
 993     // Blocks and waits for an incoming client connection and accepts it
 994     // 阻塞并等待即将到来的客户端连接并且接收它
 995     LogMessage(env, obj, "Waiting for a client connection...");
 996     int clientSocket = accept(sd, NULL, NULL);
 997 
 998     // If client socket is not valid 如果客户端 socket 无效
 999     if (-1 == clientSocket)
1000     {
1001         // Throw an exception with error number 抛出带错误号的异常
1002         ThrowErrnoException(env, "java/io/IOException", errno);
1003     }
1004 
1005     return clientSocket;
1006 }
1007 
1008 void Java_com_apress_echo_LocalEchoActivity_nativeStartLocalServer(
1009         JNIEnv* env,
1010         jobject obj,
1011         jstring name)
1012 {
1013     // Construct a new local UNIX socket. 构造一个新的本地 UNIX socket
1014     int serverSocket = NewLocalSocket(env, obj);
1015     if (NULL == env->ExceptionOccurred())
1016     {
1017         // Get name as C string 以 C 字符串形式获取名称
1018         const char* nameText = env->GetStringUTFChars(name, NULL);
1019         if (NULL == nameText)
1020             goto exit;
1021 
1022         // Bind socket to a port number 绑定 socket 到某一端口号
1023         BindLocalSocketToName(env, obj, serverSocket, nameText);
1024 
1025         // Release the name text 释放名称文本
1026         env->ReleaseStringUTFChars(name, nameText);
1027 
1028         // If bind is failed 如果绑定失败
1029         if (NULL != env->ExceptionOccurred())
1030             goto exit;
1031 
1032         // Listen on socket with a backlog of 4 pending connections
1033         // 监听有 4 个挂起连接的带 socket 的 socket
1034         ListenOnSocket(env, obj, serverSocket, 4);
1035         if (NULL != env->ExceptionOccurred())
1036             goto exit;
1037 
1038         // Accept a client connection on socket 接受 socket 的一个客户连接
1039         int clientSocket = AcceptOnLocalSocket(env, obj, serverSocket);
1040         if (NULL != env->ExceptionOccurred())
1041             goto exit;
1042 
1043         char buffer[MAX_BUFFER_SIZE];
1044         ssize_t recvSize;
1045         ssize_t sentSize;
1046 
1047         // Receive and send back the data 接收并发送回数据
1048         while (1)
1049         {
1050             // Receive from the socket 从 socket 中接收
1051             recvSize = ReceiveFromSocket(env, obj, clientSocket,
1052                     buffer, MAX_BUFFER_SIZE);
1053 
1054             if ((0 == recvSize) || (NULL != env->ExceptionOccurred()))
1055                 break;
1056 
1057             // Send to the socket 发送给 socket
1058             sentSize = SendToSocket(env, obj, clientSocket,
1059                     buffer, (size_t) recvSize);
1060 
1061             if ((0 == sentSize) || (NULL != env->ExceptionOccurred()))
1062                 break;
1063         }
1064 
1065         // Close the client socket 关闭客户端 socket
1066         close(clientSocket);
1067     }
1068 
1069 exit:
1070     if (serverSocket > 0)
1071     {
1072         close(serverSocket);
1073     }
1074 }
Echo.cpp

activity_echo_client.xml

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <EditText
 8         android:id="@+id/ip_edit"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:hint="@string/ip_edit" >
12 
13         <requestFocus />
14 
15     </EditText>
16 
17     <EditText
18         android:id="@+id/port_edit"
19         android:layout_width="match_parent"
20         android:layout_height="wrap_content"
21         android:hint="@string/port_edit"
22         android:inputType="number" />
23     
24     <EditText
25         android:id="@+id/message_edit"
26         android:layout_width="match_parent"
27         android:layout_height="wrap_content"
28         android:hint="@string/message_edit" />
29    
30     <Button
31         android:id="@+id/start_button"
32         android:layout_width="wrap_content"
33         android:layout_height="wrap_content"
34         android:text="@string/start_client_button" />
35 
36     <ScrollView
37         android:id="@+id/log_scroll"
38         android:layout_width="match_parent"
39         android:layout_height="0dip"
40         android:layout_weight="1.0" >
41 
42         <TextView
43             android:id="@+id/log_view"
44             android:layout_width="match_parent"
45             android:layout_height="wrap_content" />
46     </ScrollView>
47 
48 </LinearLayout>
activity_echo_client.xml

activity_echo_server.xml

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <LinearLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         android:orientation="horizontal">
11         
12         <EditText
13             android:id="@+id/port_edit"
14             android:layout_width="wrap_content"
15             android:layout_height="wrap_content"
16             android:layout_weight="1"
17             android:hint="@string/port_edit"
18             android:inputType="number" >
19 
20             <requestFocus />
21         </EditText>
22 
23         <Button
24             android:id="@+id/start_button"
25             android:layout_width="wrap_content"
26             android:layout_height="wrap_content"
27             android:layout_weight="1"
28             android:text="@string/start_server_button" />
29 
30     </LinearLayout>
31 
32     <ScrollView
33         android:id="@+id/log_scroll"
34         android:layout_width="match_parent"
35         android:layout_height="match_parent" >
36 
37         <TextView
38             android:id="@+id/log_view"
39             android:layout_width="match_parent"
40             android:layout_height="wrap_content" />
41     </ScrollView>
42 
43 </LinearLayout>
activity_echo_server.xml

activity_local_echo.xml

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <EditText
 8         android:id="@+id/port_edit"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:hint="@string/local_port_edit" >
12 
13         <requestFocus />
14 
15     </EditText>
16 
17     <EditText
18         android:id="@+id/message_edit"
19         android:layout_width="match_parent"
20         android:layout_height="wrap_content"
21         android:hint="@string/message_edit" />
22    
23     <Button
24         android:id="@+id/start_button"
25         android:layout_width="wrap_content"
26         android:layout_height="wrap_content"
27         android:text="@string/start_client_button" />
28 
29     <ScrollView
30         android:id="@+id/log_scroll"
31         android:layout_width="match_parent"
32         android:layout_height="0dip"
33         android:layout_weight="1.0" >
34 
35         <TextView
36             android:id="@+id/log_view"
37             android:layout_width="match_parent"
38             android:layout_height="wrap_content" />
39     </ScrollView>
40 
41 </LinearLayout>
activity_local_echo.xml

Echo Manifest.xml

 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 2     package="com.apress.echo"
 3     android:versionCode="1"
 4     android:versionName="1.0" >
 5 
 6     <uses-sdk
 7         android:minSdkVersion="8"
 8         android:targetSdkVersion="15" />
 9 
10     <uses-permission android:name="android.permission.INTERNET" />
11 
12     <application
13         android:icon="@drawable/ic_launcher"
14         android:label="@string/app_name"
15         android:theme="@style/AppTheme" >
16         
17         <activity
18             android:name=".EchoServerActivity"
19             android:label="@string/title_activity_echo_server"
20             android:launchMode="singleTop" >
21             <intent-filter>
22                 <action android:name="android.intent.action.MAIN" />
23 
24                 <category android:name="android.intent.category.LAUNCHER" />
25             </intent-filter>
26         </activity>
27         
28         <activity
29             android:name=".EchoClientActivity"
30             android:label="@string/title_activity_echo_client"
31             android:launchMode="singleTop" >
32             <intent-filter>
33                 <action android:name="android.intent.action.MAIN" />
34 
35                 <category android:name="android.intent.category.LAUNCHER" />
36             </intent-filter>
37         </activity>
38         <activity
39             android:name=".LocalEchoActivity"
40             android:label="@string/title_activity_local_echo" >
41             <intent-filter>
42                 <action android:name="android.intent.action.MAIN" />
43 
44                 <category android:name="android.intent.category.LAUNCHER" />
45             </intent-filter>
46         </activity>
47     </application>
48 
49 </manifest>
Manifest.xml

 

posted @ 2015-07-08 16:38  壬子木  阅读(999)  评论(0)    收藏  举报