Echo Socket例子项目
? 這個(gè)例子提供如下:
????? 定義必要的配置sockets的參數(shù)的一個(gè)簡單的用戶接口。
???? service邏輯對(duì)于一個(gè)見到的echo服務(wù)重復(fù)這接收到的字節(jié)返回給發(fā)送者。
??? 模塊化原生代碼片段來對(duì)于Android的原生層方便socket編程。
?? 一個(gè)面向連接的socket通信例子。
?? 一個(gè)無連接的通信例子。
?? 一個(gè)本地的socket通信例子。
? 建立一個(gè)Echo的Android項(xiàng)目。
?
??? Abstractt Echo Activity:
?? 為了重復(fù)利用者普通的功能,將會(huì)創(chuàng)建一個(gè)抽象的activity類在定義這時(shí)間activity之前。使用Project Explorer 視圖,打開src目錄,旋轉(zhuǎn)com.apress.echo包,和選擇new-》class。設(shè)置名字為AbstractEchoActivity和點(diǎn)擊這完成按鈕。該新文件的內(nèi)容如下:
package com.apress.echo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
/**
* Abstract echo activity object.
*
* @author Onur Cinar
*/
public abstract class AbstractEchoActivity extends Activity implements
OnClickListener {
/** Port number. */
protected EditText portEdit;
/** Server button. */
protected Button startButton;
/** Log scroll. */
protected ScrollView logScroll;
/** Log view. */
protected TextView logView;
/** Layout ID. */
private final int layoutID;
/**
* Constructor.
*
* @param layoutID
Communication
* layout ID.
*/
public AbstractEchoActivity(int layoutID) {
this.layoutID = layoutID;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layoutID);
portEdit = (EditText) findViewById(R.id.port_edit);
startButton = (Button) findViewById(R.id.start_button);
logScroll = (ScrollView) findViewById(R.id.log_scroll);
logView = (TextView) findViewById(R.id.log_view);
startButton.setOnClickListener(this);
}
public void onClick(View view) {
if (view == startButton) {
onStartButtonClicked();
}
}
/**
* On start button clicked.
*/
protected abstract void onStartButtonClicked();
/**
* Gets the port number as an integer.
*
* @return port number or null.
*/
protected Integer getPort() {
Integer port;
try {
port = Integer.valueOf(portEdit.getText().toString());
} catch (NumberFormatException e) {
port = null;
}
return port;
}
/**
* Logs the given message.
*
* @param message
* log message.
*/
protected void logMessage(final String message) {
runOnUiThread(new Runnable() {
public void run() {
logMessageDirect(message);
}
});
}
/**
* Logs given message directly.
*
* @param message
* log message.
*/
protected void logMessageDirect(final String message) {
logView.append(message);
logView.append("\n");
logScroll.fullScroll(View.FOCUS_DOWN);
}
/**
* Abstract async echo task.
*/
protected abstract class AbstractEchoTask extends Thread {
/** Handler object. */
private final Handler handler;
/**
* Constructor.
*/
public AbstractEchoTask() {
handler = new Handler();
}
/**
* On pre execute callback in calling thread.
*/
protected void onPreExecute() {
startButton.setEnabled(false);
logView.setText("");
}
public synchronized void start() {
onPreExecute();
super.start();
}
public void run() {
onBackground();
handler.post(new Runnable() {
public void run() {
onPostExecute();
}
});
}
/**
* On background callback in new thread.
*/
protected abstract void onBackground();
/**
* On post execute callback in calling thread.
*/
protected void onPostExecute() {
startButton.setEnabled(true);
}
}
static {
System.loadLibrary("Echo");
}
?? AbstractEchoActivity,除了處理houskeeping任務(wù)外例如綁定這用戶接口組件,提供一個(gè)簡單的線程實(shí)現(xiàn)能夠使應(yīng)用程序來執(zhí)行這網(wǎng)絡(luò)操作在單獨(dú)的線程而不是UI線程。
? Echo項(xiàng)目的字符串資源:
? <resources>
<string name="app_name">Echo</string>
<string name="title_activity_echo_server">Echo Server</string>
<string name="port_edit">Port Number</string>
<string name="start_server_button">Start Server</string>
<string name="title_activity_echo_client">Echo Client</string>
<string name="ip_edit">IP Address</string>
<string name="start_client_button">Start Client</string>
<string name="send_button">Send</string>
<string name="message_edit">Message</string>
<string name="title_activity_local_echo">Local Echo</string>
<string name="local_port_edit">Port Name</string>
? 原生的Echo模塊:
? 這原生的echo模塊將提供者原生socket接口方法對(duì)于這Java應(yīng)用程序的實(shí)現(xiàn)。使用Project Explorer,展開jni目錄對(duì)于原生源文件,和雙擊這Echo.cpp C++源文件。代替它的內(nèi)容如下:
? // 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>
Download at http://www.pin5i.com/
216 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication
// 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.
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);
Download at http://www.pin5i.com/
217 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication
// 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);
?
?
通過這通信的生命周期,和處理這排序和錯(cuò)誤檢查括號(hào)來自這應(yīng)用程序。將修改者例子Echo應(yīng)用程序來包含Tcp服務(wù)器和客戶端activites為了占建立的連接和信息交換使用sockets。
Echo Server Activity布局
?使用Project Explorer視圖,展開res目錄。展開layout子目錄,和創(chuàng)建一個(gè)新的布局文件叫做Activity_echo_server.xml.如下:
?? <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
Download at http://www.pin5i.com/
219 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication
<EditText
android:id="@+id/port_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/port_edit"
android:inputType="number" >
<requestFocus />
</EditText>
<Button
android:id="@+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/start_server_button" />
</LinearLayout>
<ScrollView
android:id="@+id/log_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/log_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
這個(gè)Echo Server提供了一個(gè)簡單的用戶接口來獲得端口來綁定服務(wù)器和來呈現(xiàn)著狀態(tài)更新來著這原生Tcp 服務(wù)區(qū)在它正在執(zhí)行。
Echo Server Activity:
?? 使用創(chuàng)建EchoServerAcitivity.java在src目錄下。
package com.apress.echo;
/**
* Echo server.
*
* @author Onur Cinar
*/
public class EchoServerActivity extends AbstractEchoActivity {
/**
* Constructor.
*/
public EchoServerActivity() {
super(R.layout.activity_echo_server);
}
protected void onStartButtonClicked() {
Integer port = getPort();
if (port != null) {
ServerTask serverTask = new ServerTask(port);
serverTask.start();
}
}
/**
* Starts the TCP server on the given port.
*
* @param port
* port number.
* @throws Exception
*/
private native void nativeStartTcpServer(int port) throws Exception;
/**
* Starts the UDP server on the given port.
*
* @param port
* port number.
* @throws Exception
*/
private native void nativeStartUdpServer(int port) throws Exception;
/**
* Server task.
*/
private class ServerTask extends AbstractEchoTask {
/** Port number. */
private final int port;
/**
* Constructor.
*
* @param port
* port number.
*/
public ServerTask(int port) {
this.port = port;
}
protected void onBackground() {
logMessage("Starting server.");
try {
nativeStartTcpServer(port);
} catch (Exception e) {
logMessage(e.getMessage());
}
logMessage("Server terminated.");
}
}
}
?? 實(shí)現(xiàn)原生TCP服務(wù):
? 使用Project Explorer,選擇這EchoServerActivity,和產(chǎn)生C和C++頭文件。打開Echo.cpp源文件。出入include語句,如下:
? #include "com_apress_echo_EchoServerActivity.h"
??? 創(chuàng)建一個(gè)Socket:socket
? 一個(gè)socket通過一個(gè)叫做socket描述符的整形術(shù)來代表。Socket API函數(shù),而不是這創(chuàng)建這socket本身,需要一個(gè)有效的socket描述符對(duì)于這個(gè)函數(shù)。一個(gè)socket函數(shù)被創(chuàng)建使用這socket函數(shù)。
?? int socket(int domain,int type,int protocol);
? 這個(gè)socket函數(shù)需要如下參數(shù)來創(chuàng)建一個(gè)新的socket:
? Domain指定這socketdomain,通信選擇的協(xié)議族。在這寫的期間,接下來的協(xié)議族被Android平臺(tái)所支持。
? PF_LOCAL:Host-interanl通信協(xié)議。這個(gè)通信協(xié)議能夠使用應(yīng)用程序那運(yùn)行在同樣的設(shè)備來使用Socket APIs來相互通信。
? PF_INET:internet 版本4端口協(xié)議。這個(gè)端口使應(yīng)用協(xié)議和正在這個(gè)網(wǎng)絡(luò)運(yùn)行的的應(yīng)用程序相互通信。
? Type指定了通信的語義。接下的主要的socket類型被支持的:
?????? SOCKET_STREAM:流端口類型同基于連接的通信使用Tcp端口。
????? SOCKET_DGRAM:數(shù)據(jù)報(bào)類型提供了無連接的通信使用UDP端口。
??? Protocol指定了將要被使用的端口。對(duì)于大部分的端口協(xié)議族和類型,僅僅只用一種坑能的端口被使用。為了選擇默認(rèn)的端口,這個(gè)參數(shù)被設(shè)置為0.
??? socket函數(shù)返回相關(guān)聯(lián)的socket描述符;-1和錯(cuò)誤errno的全局變量被設(shè)置到這恰當(dāng)?shù)膃rror。
?? NewTcpSocket幫助函數(shù)對(duì)于Echo.cpp原生模塊
* @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;
}
??? 這個(gè)幫助函數(shù)創(chuàng)建了一個(gè)新的TCP socket和失敗是拋出一個(gè)java.lang.IOException異常。
? 綁定這Socket到一個(gè)地址:bind
? 當(dāng)一個(gè)Socket被創(chuàng)建通過socket函數(shù),它存在一個(gè)socket協(xié)議族空間沒有一個(gè)端口地址分配給它。對(duì)于客戶端能夠指定和連接到這個(gè)端口,它需要首先綁定一個(gè)地址。一個(gè)socket能夠綁定到一個(gè)地址使用bind函數(shù)。
?? int bind(int socketDescriptor,const struct sockaddr* address,socklen_t addressLength);
?? 這綁定函數(shù)需要如下參數(shù)為了綁定著socket到一個(gè)地址:
?? 這socket描述符指定socket實(shí)例將綁定到這給定的地址。
? 這地址指定了端地址,socket將會(huì)綁定的。
? 這地址長度指定了端口地址結(jié)構(gòu)的長度被傳遞給這個(gè)函數(shù)。
? 取決于端口地址,一個(gè)不同特色的端口地址被使用。對(duì)于PF_INET端口協(xié)議,這socketaddr_in結(jié)構(gòu)體被使用來指定和端口地址。這socketaddr_in結(jié)構(gòu)體的定義如下:
??? struct sockaddr_in {
sa_family_t sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
}
? 如果這個(gè)端口被恰當(dāng)?shù)慕壎?#xff0c;和bind函數(shù)將返回0;否則,它將返回-1和這errno全局變量到這恰當(dāng)錯(cuò)誤。
?? 使用這Editor視圖,添加這BindSocketToPort幫助函數(shù)到Echo.cpp本地模塊源文件如下:
??? /**
* 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);
}
}
?? 如果端口號(hào)被設(shè)定為0在地址結(jié)構(gòu)體中,這bind函數(shù)將分配第一個(gè)可能的端口號(hào)給這個(gè)socket。端口號(hào)可以被獲得通過使用getsocketname函數(shù)。如下:
??? /**
* 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;
Download at http://www.pin5i.com/
225 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication
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;
}
?
???? 你可能注意到,這個(gè)端口號(hào)并沒有直接的傳遞到sockaddr_in結(jié)構(gòu)體。代替的是,這htons函數(shù)被用來首先做轉(zhuǎn)換。這是由于host和網(wǎng)絡(luò)字節(jié)的排序的不同。
網(wǎng)絡(luò)字節(jié)的排序:
??? 不同的機(jī)器架構(gòu)使用不同的規(guī)則對(duì)于數(shù)據(jù)的排序在硬件水平。下面是著名的字節(jié)順序:
??? 大端排序:先存儲(chǔ)大端字節(jié)。
?? 小端排序:先存儲(chǔ)最后有意義的字節(jié)。
帶有不同字節(jié)排序的規(guī)則的機(jī)器不能直接交換數(shù)據(jù)。為了使用帶有不同字節(jié)排序規(guī)則的機(jī)器相互通信通過網(wǎng)絡(luò),這internet端口定義了大端排序作為官方的網(wǎng)絡(luò)字節(jié)排序轉(zhuǎn)換對(duì)于數(shù)據(jù)的轉(zhuǎn)換。
?? 正如Java虛擬機(jī)已經(jīng)使用了大端字節(jié)排序,這第一次你正聽說
字節(jié)序數(shù)據(jù)。Java應(yīng)用程序并沒有做任何轉(zhuǎn)換對(duì)于數(shù)據(jù)當(dāng)在網(wǎng)絡(luò)上傳輸數(shù)據(jù)時(shí)。相比而言,對(duì)于原生的組件不能被執(zhí)行在Java虛擬機(jī)上,他們使用的機(jī)器自己排序如下:
????? ARM和x86機(jī)器架構(gòu)使用小端字節(jié)排序。
????? MIPS機(jī)器架構(gòu)使用達(dá)到字節(jié)排序。
當(dāng)通過網(wǎng)絡(luò)通信,這原生的代碼必須在機(jī)器字節(jié)排序和網(wǎng)絡(luò)字節(jié)排序鏡像轉(zhuǎn)換。
這Socket庫提供了一套轉(zhuǎn)換函數(shù)來使原生的應(yīng)用程序透明的操作字節(jié)順序的轉(zhuǎn)換。這些函數(shù)被定義通過這sys/endian.h頭文件:
???? #include<sys/endian.h>
? 這接下轉(zhuǎn)換函數(shù)被提供如下:
?? htons函數(shù)轉(zhuǎn)換一個(gè)無符號(hào)short從host機(jī)器字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序。
?? ntohs函數(shù)和htons相反,通過轉(zhuǎn)換一個(gè)無符號(hào)的short類型數(shù)據(jù)從網(wǎng)絡(luò)到host機(jī)器的字節(jié)順序。
?? htonl函數(shù)轉(zhuǎn)換一個(gè)無符號(hào)的整形術(shù)從host機(jī)器字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序。
?? ntohl函數(shù)和htonl函數(shù)相反。
? 監(jiān)聽到來的連接:listen
?? 通過listen函數(shù)監(jiān)聽一個(gè)socket:
? int listen(int socketDescriptor,int backlog);
?? 這個(gè)監(jiān)聽函數(shù)需要如下的參數(shù)被提供來開始監(jiān)聽到來的連接:
?????? 這socket描述符指定了socket實(shí)例這應(yīng)用程序想要開始監(jiān)聽到來的連接。
????? 這backlog指定這隊(duì)列的長度來保存著pending到來的連接。如果應(yīng)用程序時(shí)繁忙的服務(wù)一個(gè)客戶端,其他到來的連接排隊(duì)到一定數(shù)量的pengding有backlog。當(dāng)backlog是滿時(shí),連接被拒絕。
如果函數(shù)成功返回0,否則-1;
/**
* 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);
}
}
接收到來的連接:accept
? 這accept函數(shù)被明確的用來放置到來的連接從監(jiān)聽隊(duì)列到接收它。
? int accept(int socketDescriptor,struct sockeaddr* address,socklen_t* addressLength);
?這個(gè)accept函數(shù)時(shí)一個(gè)阻塞函數(shù)。如果沒有阻塞到來的連接請(qǐng)求在監(jiān)聽隊(duì)列,它放置著調(diào)用線程到一個(gè)阻塞的狀態(tài)知道一個(gè)新到來的連接到達(dá)。這accept函數(shù)需要的參數(shù):
? ? 這socket 描述符指定了這socket的實(shí)例,應(yīng)用程序想要接收一個(gè)pending的到來的連接。
? ? ?這地址的長度指針提供了一個(gè)地址結(jié)構(gòu),填充了要連接的客戶端的端口地址。如果信息不被應(yīng)用程序所需要,它被設(shè)置為null。
? ? 這地址的長度指針同了連接客戶端端口地址的長度要分配的內(nèi)存空間。如果信息不被需要?jiǎng)t設(shè)置為NULL。
accept請(qǐng)求成功,這函數(shù)返回客戶端描述符,否則為-1和errno全局變量被設(shè)置。
? ? LogAddress幫助函數(shù)到原生模塊源文件,如下:
? ?/**
* 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))
Download at http://www.pin5i.com/
229 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication?
{
// 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);
}
}
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)
Download at http://www.pin5i.com/
230 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication?
{
// Throw an exception with error number
ThrowErrnoException(env, "java/io/IOException", errno);
}
else
{
// Log address
LogAddress(env, obj, "Client connection from ", &address);
}
return clientSocket;
}
recv函數(shù)是一個(gè)阻塞函數(shù)。如果沒有數(shù)據(jù)從給定的socket被接收到,它將放著調(diào)用進(jìn)程到阻塞狀態(tài)直到數(shù)據(jù)可以使用。這recv函數(shù)需要的參數(shù)如下:
? ?這socket描述符指定了socket實(shí)例,應(yīng)用程序想要接收數(shù)據(jù)的socket實(shí)例。
? ? 這buffer指針是將要填充接收到數(shù)據(jù)的內(nèi)存地址。
? ? Flages中指定了可惜標(biāo)記。
如果recv函數(shù)成功,它將返回接收到數(shù)據(jù)的字節(jié)數(shù)目;否則,它將返回-1和errno全局變量被設(shè)置恰當(dāng)?shù)腻e(cuò)誤。如果函數(shù)返回0,它將指示socket是斷開連接的。使用如下:
??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;
}
發(fā)送數(shù)據(jù)到Socket:send
發(fā)送數(shù)據(jù)到socket通過發(fā)送函數(shù):
? ? ?ssize_t send(int socketDescriptor,void* buffer,size_t bufferLeng,int flags);
像recv函數(shù),這發(fā)送函數(shù)也是個(gè)阻塞函數(shù)。如果這socket是繁忙的發(fā)送數(shù)據(jù),它放置調(diào)用進(jìn)程到一個(gè)阻塞狀態(tài)直到對(duì)于傳輸?shù)臄?shù)據(jù)準(zhǔn)備好。這發(fā)送函數(shù)需要接下的參數(shù)被提供為了及時(shí)一個(gè)pending到來的連接:
? ? ?這socket描述符指定了應(yīng)用程序想要發(fā)送數(shù)據(jù)給的socket實(shí)例
? ? ?這buffer指針指向了要發(fā)送數(shù)據(jù)的內(nèi)存地址。
? ? 這buffer長度指定了這buffer的大小。這send函數(shù)僅僅傳輸者buffer到這個(gè)長度和返回。
? ? Flages指定額外的發(fā)送標(biāo)記。
? ? send函數(shù)返回傳輸?shù)牡淖止?jié)數(shù)據(jù)如果成功,否則-1和制定了恰當(dāng)錯(cuò)誤的全局變量errno。像recvSendToSocket幫助函數(shù),如下:
? ??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);
Download at http://www.pin5i.com/
233 CHAPTER 8: POSIX Socket API: Connection-Oriented Communication?
// 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;
}
? ? ?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Echo Socket例子项目的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POSIX线程的同步
- 下一篇: Socket UDP无连接通信