BREW network and socket guidelines

xiaoxiao2021-03-06  39

Network and socket guidelines September 12, 2003 Author: Qualcomm mobile front line

Thread problems developers usually use the block call when using the network in the application on Windows or UNIX platforms. Such calls are only returned when the operation is completed or fails. For example, a block write call is usually used. It only returns when all data is successfully sent or errors. Similarly, many programmers use FGETS () and other calls to the edit block socket. It will wait for the receiving full line. For the local I / O (such as disk read / write), the block socket is similar to the block call, thus being widely used. However, different from the local disk I / O, the network will be inherited to be restricted by unforeseen delays. These delays may occur when performing any operation, and may last for a few minutes. To prevent applications from being locked and do not respond to user events or other events when blocking blocks, developers generally use threads; usually, each network is connected to a thread. Like the use of the block call, the thread has the advantages of familiar linear model, but will increase overhead and complexity. In addition to memory and other system resources, threads use less distinct resources, such as structural spaces for maintaining status, synchronization, etc. The thread will greatly increase the complexity of the application, thereby increasing code and memory. The more complex the application, the higher the chance of failure. The thread will cause synchronization problems when accessing the shared object. There are many traps only in this area. BREWTM's callback, no compliance, in a model that considers the use of a simpler program, is therefore easier to write and debug (thus more stable), and more resources are used. Because BREW does not support threads, the BREW API is more simple (creating, destroying, start, stop, and synchronous threads). The BREW layer itself is smaller, more efficient and simpler (therefore more reliable). In this way, when transplanting BREW to new chip sets and handheld devices, it will shorten the required time, reduce the cost of cost, saving the required energy. Applications (in many cases, BREW layer itself) will benefit from a variety of multi-threaded problems. The problem includes:

Multi-threaded access to shared data is difficult (or unable) to fully test multi-thread code, especially in cross-development environments, in a block-in-block call, in the case - usually in these cases Some situations are ignored, so threads (in turn) cannot be safely terminated without certain random delays. This is a problem in the environment where mobile handheld equipment resources are strict. In general, there is less memory for use (especially the stack memory), and the processing interrupt is easier and concise. A potential difficulties associated with the unparalleled API include processing a large block existing code written as a block API. However, any module network code can "mechanically" convert into a no block version. It is not currently available in software to perform this sense of operation, but the process can reflect a mechanical in the sense of the same complex software as compilers. The conversion process may be very time consuming, but if the developer clearly understands and pays attention to basic equivalent concepts, even a larger code base can also be successfully converted. The conversion does not have a block code conversion process uses object-oriented terms, but can be implemented using C or C as needed. Each module function - that is, a function directly or indirectly waiting for an external event - can be converted to an object (member function) having three or more methods:

Constructor Decorative Function Function The following operations should be able to ensure effective conversion:

Define an object / structure with persistent state. Life span block operation All value local variables must be moved to the object (this may include the variable of the original function). Because the object is assigned on the heap, its members still exist when returning to control. Create a "new" function to assign and initialize the status object. This function is passed to the parameter of the original block function as a parameter. It also has two new parameters (function pointer and empty pointer) describing the callback function called when completed. Create a "work" function that contains the main part of the original module function. It has a parameter: a pointer to the status object. The work function is scheduled or called by the constructor. The work function is a state machine: it uses the current status value to perform the switch statement. It contains the case indication of each state. Each operation series separated by the block call in the original code has a state (and other status). When calling, the work function will continue to handle the task of the original function until it must wait for the event (for example, a network connection). Then, it saves the state in the object, requests the callback from the appropriate mechanism and returns. When called again, it continues from the last stop position (using the status value stored in the object as the index of its switch statement). This requires the following modifications to the main part of the function: Each module call is started, the scheduling function itself is reused, record the current state, record the current state, and replace the control return program. Each state has a case statement mark. For example: WaitonKeyPress ();

Being: KeyPressNotify (ME, Object_Work);

ME-> nState = ST_WAITFORKEY;

Return;

In the case where the callback function is not matched with the prototype of the work function, you can use a smaller assistant function. The assistant function matches the informational callback function prototype, only the power function is called. For example, pass the result value to the BREW Connect () callback function. Assistant functions can store result values ​​in the status object and call a work function. The call for each previously encoded block is given a status value. At the beginning of the work function, the switch using the status variable will guide the control flow to the corresponding tag. According to the situation, only the switch statement will execute a goto statement for each state. This allows you to keep the original structure of the block code (including "for" and "while" loop). However, moving the code to the switch statement is generally easier to keep and verify. (If the code of a situation statement is longer, you can move it to the Inline function.) Example: Switch (me-> nstate)

{

...

Case ST_WAITFORKEY:

...

Break;

...

}

The work function is usually called by the BREW when the event is completed, and the original function is only called in the original application execution process. Therefore, the work function calls a callback function to notify its client ("call program") instead of exiting the returns of the caller and restores the original control stream (such as a block function). If the original function returns a value, it is best to handle the pointer to the constructor to pass a result storage location. A "delete" function destroys the object - stop the operation at any time - release all the resources allocated and cancel any operation that may be scheduled. This function should also be called by the work function to clear any resources before calling the client backup function. Because the BREW does not have a thread and reusable function, the delete function can only be called by the client when the work function is not activated (the callback function is dispatched and exits). This simplifies the delete and work function, because the work function does not need to lock any content or test for the cancel operation, and the Delete function must only be cleared (including the callback function requesting the power function request) and the release resource. The client (original call program) may respond to the user who press the "Cancel" button, call the delete function directly, and interrupt the operation in the middle. The conversion example below is a conversion example. Colors represent some key components of the original function and the corresponding part in the new version. Persistent Variables (Green) Call / No Block Call Conversion (Blue) Non-Block Version NPRS / Code (red) Original Block Version: The following is a simple block function for a longer execution time. It omits some "#define" and errors and hide many unrelated features. A "empty" function simplifies this example. (The function of returning value first should be converted to an empty function.) Void querydns (const char * pszdomain, inaddr * paddrresult)

{

Char * pcReq = null;

INT CBREQ = 0;

INT cnttribries = 0;

INT S = Socket ();

INT NRCVD = 0;

Char BUF [512];

PCREQ = Malloc (300);

CBREQ = DNSCONStructRequest (PCREQ, PSZDOMAIN);

DO

{

INADDR ADDR;

Inport port;

Sendto (S, DNSADDR, DNSPORT, PCREQ, CBREQ);

// Recvfrom_timeout: Assuming blocking function,

// Similar to Recvfrom (), but has a clear timeout value

Nrcvd = recvfrom_timeout (s, & addr, & port, 3000,

BUF, SIZEOF (BUF);

}

While (nrcvd == Timeout && cnttribates

IF (nrcvd> 0)

{

* paddrresult = DNSReadResult (buf, nrcvd);

}

#ELSE

{

* paddrresult = inaddr_none;

}

Free (PCREQ);

Close (s);

}

RESULTS: The result is the following class definition (here, using an object-oriented C code) is used. Used GOTO form instead of embedding the code into the Switch statement because it requires continued use of more original control flows (including loops), so more clearly. However, if you don't use GOTO, you will be easier and easier to maintain and debug it easier to use GOTO. Note: It is still tolerated to perform many obvious optimization operations (for example, by moving the status 0 work function to the NEW function to cancel status variables and switches, the two memory allocations can be merged into one, and some persistent variables can be canceled. Because this example is used to explain the consistency of the conversion process, these optimized operations can be omitted. For asynchronous socket operations, use completion callback interface instead of the BREW socket API to return to the interface to explain the more general cases. However, the difference is small. Typedef struct {

Int nstate;

Client_callback * paalldone;

Void ** PPClientPtr;

Char * pcReq;

int CBREQ;

int CNTTRIES;

INADDR * PADDRRRESULT;

Int nrcvd;

Int S;

Char BUF [512];

} Querydns;

// Create an object; here contain all initialization code

//

Querydns * querydns_new (const char * pszdomain,

INADDR * PADDRRESULT,

Client_callback * PallDone,

void ** PPClientPtr)

{

QueryDns * ME = (querydns *) Malloc (querydns));

ME-> PCREQ = Malloc (300);

ME-> CBREQ = DNSCONSTRUCTREQEST (ME-> PCREQ,

pszdomain;

ME-> cnttribries = 0;

ME-> nState = 0;

ME-> Paddrresult = PaddrRRRRESULT;

ME-> paldone = paldone;

ME-> ppclientptr = ppclientptr;

ME-> s = socket ();

QueryDns_Work (me);

}

/ / Delete the object; here contain all clear code

//

QueryDns_delete (querydns * me)

{

// Recvfrom_timeout_cancel () and sendto_cancel are assumed // unpacking functions of receiving and transmitting operations. Recvfrom_timeout_cancel (me, querydns_work); sendto_cancel (me, querydns_work); close (me-> s); free (me);} void querydns_work (void * pvcxt) {// Create PVCXT conversion Variables, not // must be converted for Work () function pointer querydns * me = (querydns *) pvcxt; switch (me-> nState) {case 1: goto querydns_st_1; case 2: goto querydns_st_2; // case 0: failed } do {iNAddr addr; INPort port; sendto_asynch (me-> s, DNSADDR, DNSPORT, pcReq, cbReq, ​​me, QueryDNS_Work); me-> nState = 1; return; querydns_st_1: // recvfrom_timeout_asynch: // assumed successful call Or timeout reparted function recvfrom_timeout_asynch (me-> s, & addr, & port, 3000, me-> buf, sizeof (me-> buf), & me-> nrcvd, me, querydns_work); me-> nState = 2; Return Querydns_St_2:} while (me-> nrcvd == timeout && cnttrib " nrcvd> 0) {* me-> paddrresult = DNSReadResult (me-> buf, me-> nrcvd); } else {* me-> paddrresult = INADDR_NONE;} me-> paldone (me-> ppclientptr); querydns_delete (me);} Usage difference: The calling program does not call queryDNS () and expect it to return the result is valid, but use : Me-> pqdns = queryd NS_NEW (Pszdomain, & Me-> Addr, Me, MyObj_dnsdone);

When you call myobj_dnsdone (), ME-> Addr saves the result. Finally, the calling program should correctly support the cancellation of the operation. This includes:

When calling the callback function, set the me-> pqdns to NULL, do the following: if (me-> pqdns! = Null)

{

Querydns_delete (me-> pqdns);

ME-> pqdns = null;

}

转载请注明原文地址:https://www.9cbs.com/read-62099.html

New Post(0)