在Xlib程序中无阻塞的并行处理X事件和网络通信的方法可以使用select函数的基本思想来实现。Xlib库提供了一个在很多系统平台中都通用的接口函数(XtAppAddInput)来实现该功能;当然,我们也可采用自己的代码来实现。
示例程序:
/*
* Demo.c
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
/* $XFree86: xc/lib/Xaw/SimpleP.h,v 1.12 2001/12/14 19:54:43 dawes Exp $ */
#ifndef _SimpleP_h
#define _SimpleP_h
#include
//#include
/* $XFree86: xc/lib/Xaw/Simple.h,v 1.8 2001/01/17 19:42:30 dawes Exp $ */
#ifndef _Simple_h
#define _Simple_h
#include
/* Resources:
Name Class RepType Default Value
---- ----- ------- -------------
background Background Pixel XtDefaultBackground
border BorderColor Pixel XtDefaultForeground
borderWidth BorderWidth Dimension 1
cursor Cursor Cursor None
cursorName Cursor String NULL
destroyCallback Callback Pointer NULL
displayList DisplayList XawDisplayList* NULL
height Height Dimension 0
insensitiveBorder Insensitive Pixmap Gray
mappedWhenManaged MappedWhenManaged Boolean True
pointerColor Foreground Pixel XtDefaultForeground
pointerColorBackground Background Pixel XtDefaultBackground
sensitive Sensitive Boolean True
tip Tip String NULL
width Width Dimension 0
x Position Position 0
y Position Position 0
*/
#define XtNcursor "cursor"
#define XtNcursorName "cursorName"
#define XtNinsensitiveBorder "insensitiveBorder"
#define XtCInsensitive "Insensitive"
#ifndef XtCInternational
#define XtCInternational "International"
#endif
#ifndef XtNinternational
#define XtNinternational "international"
#endif
typedef struct _SimpleClassRec *SimpleWidgetClass;
typedef struct _SimpleRec *SimpleWidget;
//extern WidgetClass simpleWidgetClass;
#endif /* _Simple_h */
_XFUNCPROTOBEGIN
//#include
/* $XdotOrg: xc/lib/Xaw/XawInit.h,v 1.2 2004/04/23 18:43:38 eich Exp $ */
#ifndef _XawInit_h
#define _XawInit_h
#define XawVendor XVENDORNAMESHORT
#define XawVersion 6700002L
#include
_XFUNCPROTOBEGIN
void XawInitializeWidgetSet(void);
extern Widget XawOpenApplication(
XtAppContext *app_context_return,
Display *dpy,
Screen *screen,
String application_name,
String application_class,
WidgetClass widget_class,
int *argc,
String *argv
);
_XFUNCPROTOEND
#endif /* _XawInit_h */
typedef struct
{
Bool (*change_sensitive)(Widget);
} SimpleClassPart;
#define XtInheritChangeSensitive ((Bool (*)(Widget))_XtInherit)
typedef struct _SimpleClassRec
{
CoreClassPart core_class;
SimpleClassPart simple_class;
} SimpleClassRec;
//extern SimpleClassRec simpleClassRec;
typedef struct
{
/* resources */
Cursor cursor;
Pixmap insensitive_border;
String cursor_name; /* cursor specified by name */
Pixel pointer_fg, pointer_bg; /* Pointer colors */
Boolean international;
XtCallbackList callbacks;
/* private */
} SimplePart;
typedef struct _SimpleRec
{
CorePart core;
SimplePart simple;
} SimpleRec;
_XFUNCPROTOEND
#endif /* _SimpleP_h */
//#include
//#include "Private.h"
/* $XFree86: xc/lib/Xaw/Private.h,v 3.9 1999/05/16 10:12:48 dawes Exp $ */
#ifndef _XawPrivate_h
#define _XawPrivate_h
#define XawMax(a, b) ((a) > (b) ? (a) : (b))
#define XawMin(a, b) ((a) < (b) ? (a) : (b))
#define XawAbs(a) ((a) < 0 ? -(a) : (a))
#define XawStackAlloc(size, stk_buffer) \
((size) <= sizeof(stk_buffer) \
? (XtPointer)(stk_buffer) \
: XtMalloc((unsigned)(size)))
#define XawStackFree(pointer, stk_buffer) \
do { \
if ((pointer) != (XtPointer)(stk_buffer)) \
XtFree((char *)pointer); \
} while (0)
#ifndef XtX
#define XtX(w) (((RectObj)w)->rectangle.x)
#endif
#ifndef XtY
#define XtY(w) (((RectObj)w)->rectangle.y)
#endif
#ifndef XtWidth
#define XtWidth(w) (((RectObj)w)->rectangle.width)
#endif
#ifndef XtHeight
#define XtHeight(w) (((RectObj)w)->rectangle.height)
#endif
#ifndef XtBorderWidth
#define XtBorderWidth(w) (((RectObj)w)->rectangle.border_width)
#endif
/* misc */
void XawTypeToStringWarning(Display*, String);
/* OS.c */
int _XawGetPageSize(void);
#endif /* _XawPrivate_h */
/*
* Class Methods
*/
static Bool ChangeSensitive(Widget);
static void XawSimpleClassInitialize(void);
static void XawSimpleClassPartInitialize(WidgetClass);
static void XawSimpleRealize(Widget, Mask*, XSetWindowAttributes*);
static Boolean XawSimpleSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
/*
* Prototypes
*/
static void ConvertCursor(Widget);
/*
* Initialization
*/
#define offset(field) XtOffsetOf(SimpleRec, simple.field)
static XtResource resources[] =
{
{
XtNcursor,
XtCCursor,
XtRCursor,
sizeof(Cursor),
offset(cursor),
XtRImmediate,
(XtPointer)None
},
{
XtNinsensitiveBorder,
XtCInsensitive,
XtRPixmap,
sizeof(Pixmap),
offset(insensitive_border),
XtRImmediate,
NULL
},
{
XtNpointerColor,
XtCForeground,
XtRPixel,
sizeof(Pixel),
offset(pointer_fg),
XtRString,
XtDefaultForeground
},
{
XtNpointerColorBackground,
XtCBackground,
XtRPixel,
sizeof(Pixel),
offset(pointer_bg),
XtRString,
XtDefaultBackground
},
{
XtNcursorName,
XtCCursor,
XtRString,
sizeof(String),
offset(cursor_name),
XtRString,
NULL
},
{
XtNinternational,
XtCInternational,
XtRBoolean,
sizeof(Boolean),
offset(international),
XtRImmediate,
(XtPointer)False
},
{
XtNcallback,
XtCCallback,
XtRCallback,
sizeof(XtPointer),
offset(callbacks),
XtRCallback,
NULL
},
#undef offset
};
SimpleClassRec simpleClassRec =
{
/* core */
{
(WidgetClass)&widgetClassRec, /* superclass */
"Simple", /* class_name */
sizeof(SimpleRec), /* widget_size */
XawSimpleClassInitialize, /* class_initialize */
XawSimpleClassPartInitialize, /* class_part_initialize */
False, /* class_inited */
NULL, /* initialize */
NULL, /* initialize_hook */
XawSimpleRealize, /* realize */
NULL, /* actions */
0, /* num_actions */
resources, /* resources */
XtNumber(resources), /* num_resources */
NULLQUARK, /* xrm_class */
True, /* compress_motion */
True, /* compress_exposure */
True, /* compress_enterleave */
False, /* visible_interest */
NULL, /* destroy */
NULL, /* resize */
NULL, /* expose */
XawSimpleSetValues, /* set_values */
NULL, /* set_values_hook */
XtInheritSetValuesAlmost, /* set_values_almost */
NULL, /* get_values_hook */
NULL, /* accept_focus */
XtVersion, /* version */
NULL, /* callback_private */
NULL, /* tm_table */
XtInheritQueryGeometry, /* query_geometry */
XtInheritDisplayAccelerator, /* display_accelerator */
NULL, /* extension */
},
/* simple */
{
ChangeSensitive, /* change_sensitive */
},
};
WidgetClass simpleWidgetClass = (WidgetClass)&simpleClassRec;
static void
XawSimpleClassInitialize(void)
{
static XtConvertArgRec convertArg[] =
{
{
XtWidgetBaseOffset,
(XtPointer)XtOffsetOf(WidgetRec, core.screen),
sizeof(Screen *)
},
{
XtResourceString,
(XtPointer)XtNpointerColor,
sizeof(Pixel)
},
{
XtResourceString,
(XtPointer)XtNpointerColorBackground,
sizeof(Pixel)
},
{
XtWidgetBaseOffset,
(XtPointer)XtOffsetOf(WidgetRec, core.colormap),
sizeof(Colormap)
},
};
XawInitializeWidgetSet();
XtSetTypeConverter(XtRString, XtRColorCursor, XmuCvtStringToColorCursor,
convertArg, XtNumber(convertArg), XtCacheByDisplay, NULL);
}
static void
XawSimpleClassPartInitialize(WidgetClass cclass)
{
SimpleWidgetClass c = (SimpleWidgetClass)cclass;
SimpleWidgetClass super = (SimpleWidgetClass)c->core_class.superclass;
if (c->simple_class.change_sensitive == NULL)
{
char buf[BUFSIZ];
(void)XmuSnprintf(buf, sizeof(buf),
"%s Widget: The Simple Widget class method "
"'change_sensitive' is undefined.\nA function "
"must be defined or inherited.",
c->core_class.class_name);
XtWarning(buf);
c->simple_class.change_sensitive = ChangeSensitive;
}
if (c->simple_class.change_sensitive == XtInheritChangeSensitive)
c->simple_class.change_sensitive = super->simple_class.change_sensitive;
}
static void
XawSimpleRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
{
Pixmap border_pixmap = CopyFromParent;
if (!XtIsSensitive(w))
{
/* change border to gray; have to remember the old one,
* so XtDestroyWidget deletes the proper one */
if (((SimpleWidget)w)->simple.insensitive_border == None)
((SimpleWidget)w)->simple.insensitive_border =
XmuCreateStippledPixmap(XtScreen(w),
w->core.border_pixel,
w->core.background_pixel,
w->core.depth);
border_pixmap = w->core.border_pixmap;
attributes->border_pixmap =
w->core.border_pixmap = ((SimpleWidget)w)->simple.insensitive_border;
*valueMask |= CWBorderPixmap;
*valueMask &= ~CWBorderPixel;
}
ConvertCursor(w);
if ((attributes->cursor = ((SimpleWidget)w)->simple.cursor) != None)
*valueMask |= CWCursor;
XtCreateWindow(w, InputOutput, (Visual *)CopyFromParent,
*valueMask, attributes);
if (!XtIsSensitive(w))
w->core.border_pixmap = border_pixmap;
}
/*
* Function:
* ConvertCursor
*
* Parameters:
* w - simple widget
*
* Description:
* Converts a name to a new cursor.
*/
static void
ConvertCursor(Widget w)
{
SimpleWidget simple = (SimpleWidget) w;
XrmValue from, to;
Cursor cursor = None;
if (simple->simple.cursor_name == NULL)
return;
from.addr = (XPointer)simple->simple.cursor_name;
from.size = strlen((char *)from.addr) + 1;
to.size = sizeof(Cursor);
to.addr = (XPointer)&cursor;
if (XtConvertAndStore(w, XtRString, &from, XtRColorCursor, &to))
simple->simple.cursor = cursor;
else
XtAppErrorMsg(XtWidgetToApplicationContext(w),
"convertFailed","ConvertCursor","XawError",
"Simple: ConvertCursor failed.",
NULL, NULL);
}
/*ARGSUSED*/
static Boolean
XawSimpleSetValues(Widget current, Widget request, Widget cnew,
ArgList args, Cardinal *num_args)
{
SimpleWidget s_old = (SimpleWidget)current;
SimpleWidget s_new = (SimpleWidget)cnew;
Bool new_cursor = False;
/* this disables user changes after creation */
s_new->simple.international = s_old->simple.international;
if (XtIsSensitive(current) != XtIsSensitive(cnew))
(*((SimpleWidgetClass)XtClass(cnew))->simple_class.change_sensitive)
(cnew);
if (s_old->simple.cursor != s_new->simple.cursor)
new_cursor = True;
/*
* We are not handling the string cursor_name correctly here
*/
if (s_old->simple.pointer_fg != s_new->simple.pointer_fg ||
s_old->simple.pointer_bg != s_new->simple.pointer_bg ||
s_old->simple.cursor_name != s_new->simple.cursor_name)
{
ConvertCursor(cnew);
new_cursor = True;
}
if (new_cursor && XtIsRealized(cnew))
{
if (s_new->simple.cursor != None)
XDefineCursor(XtDisplay(cnew), XtWindow(cnew), s_new->simple.cursor);
else
XUndefineCursor(XtDisplay(cnew), XtWindow(cnew));
}
return (False);
}
static Bool
ChangeSensitive(Widget w)
{
if (XtIsRealized(w))
{
if (XtIsSensitive(w))
if (w->core.border_pixmap != XtUnspecifiedPixmap)
XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w),
w->core.border_pixmap);
else
XSetWindowBorder(XtDisplay(w), XtWindow(w),
w->core.border_pixel);
else
{
if (((SimpleWidget)w)->simple.insensitive_border == None)
((SimpleWidget)w)->simple.insensitive_border =
XmuCreateStippledPixmap(XtScreen(w),
w->core.border_pixel,
w->core.background_pixel,
w->core.depth);
XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w),
((SimpleWidget)w)->simple.insensitive_border);
}
}
return (False);
}
/*****************************************/
void XawInitializeWidgetSet(void)
{
static Boolean firsttime = True;
if (firsttime)
{
firsttime = False;
XtInitializeWidgetClass(vendorShellWidgetClass);
}
}
/*****************************************/
static void
usr_callback(Widget w, XtPointer client_data, XtPointer call_data)
{
static int num = 0;
int pos;
int sock;
Display *dpy;
Screen *screen;
Window win;
GC gc;
XTextItem xtext;
static int x, y;
int blackColor, whiteColor;
char strBuf[64];
dpy = XtDisplay(w);
screen = XtScreen(w);
win = XtWindow(w);
blackColor = BlackPixel(dpy, DefaultScreen(dpy));
whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
gc = XCreateGC(dpy, win, 0, NULL);
XSetForeground(dpy, gc, blackColor);
//XClearWindow(dpy, win);
/*
for(pos = 0; pos < 400; pos += 8)
{
XDrawLine(dpy, win, gc, pos, 0, pos, 400);
XDrawLine(dpy, win, gc, 0, pos, 400, pos);
}
*/
*strBuf = '\0';
sprintf(strBuf, "count: %d", num);
xtext.chars = strBuf;
xtext.nchars = strlen(strBuf);
xtext.delta = 0;
xtext.font = None;
XClearArea(dpy, win, x, y-12, 32*8, 16, False); /* y-8 <-> 8 */
x = XtWidth(w) / 2;
y = XtHeight(w) / 2;
XDrawText(dpy, win, gc, x, y, &xtext, 1);
sock = *(int *)call_data; // not client_data
//printf("usr_callback 0x%x: data = %d\n", call_data, sock);
sprintf(strBuf, "%s %d", (char *)client_data, num);
if (send(sock, (const void *)strBuf, strlen(strBuf)+1, 0) == -1)
{
perror("send");
}
++num;
return;
}
/*****************************************/
static void
usr_hndlr(Widget w, XtPointer client_data, XEvent *event, Boolean *bContinue)
{
XtCallCallbacks(w, XtNcallback, client_data);
return;
}
/*****************************************/
/* Hard-code a portnumber and name of the server machine, for when wecreate
* our own socket to ourselves running as a server. */
#define PORT_NUM 3141
/* #define SERVER_NAME "hgwells.omnia.de" */
#define SERVER_NAME "localhost"
/* Define this so that the MainLoop implementation uses select(). */
#define USE_SELECT
void server_main(void);
void client_main(int argc, char **argv);
void process_client(int sock);
//void pushed(Widget w, XtPointer client, XtPointer call);
void read_sock(int sock);
void admin_server_input(XtPointer client, int *fd, XtInputId *id);
void MainLoop(XtAppContext app_cntxt, Widget toplevel, int sock);
void process_events(XtAppContext app_cntxt);
/***************************************************************************
**
* FUNCTION: main * DESCRIPTION:
****************************************************************************
*/
int main(int argc, char *argv[])
{
if (argc == 1)
server_main();
else
client_main(argc, argv);
return 0;
}
/***************************************************************************
**
* FUNCTION: server_main
* DESCRIPTION: Create a TCP/IP socket, waiting for incomingconnections.
****************************************************************************
*/
void server_main(void)
{
int rc, len, sock, newsock;
struct sockaddr_in addr;
struct hostent *host;
(void)fprintf(stderr, "Server Listener on %s...\n", SERVER_NAME);
host = gethostbyname(SERVER_NAME);
if (host == (struct hostent *)NULL)
{
perror("gethostbyname");
exit(1);
}
(void)fprintf(stderr, "creating socket\n");
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
perror("socket");
exit(1);
}
(void)memset((void *)&addr, '\0', sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NUM);
(void)memcpy((void *)&addr.sin_addr, (void *)host->h_addr,
(size_t)host->h_length);
(void)fprintf(stderr, "binding...\n");
rc = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (rc == -1)
{
perror("bind");
exit(1);
}
(void)fprintf(stderr, "listening...\n");
rc = listen(sock, 10);
if (rc == -1)
{
perror("listen");
exit(1);
}
for ( ; ; )
{
len = sizeof(addr);
(void)fprintf(stderr, "accepting...\n");
newsock = accept(sock, (struct sockaddr *)&addr, (unsigned int
*)&len);
(void)fprintf(stderr, "new socket = %d...\n", newsock);
process_client(newsock);
}
return;
}
/***************************************************************************
**
* FUNCTION: process_client
* DESCRIPTION: Read messages from the socket, sending back a simpleack.
****************************************************************************
*/
void process_client(int sock)
{
int rc;
char buf[BUFSIZ];
for ( ; ; )
{
rc = recv(sock, buf, sizeof(buf), 0);
if (rc == -1)
perror("recv");
buf[rc] = '\0';
(void)fprintf(stderr, "Recving '%s'\n", buf);
if (strcmp(buf, "bye") == 0)
break;
(void)fprintf(stderr, "Sending '+OK'\n", buf);
if (send(sock, (const void *)"+OK", 4, 0) == -1)
perror("send");
}
return;
}
/***************************************************************************
**
* FUNCTION: client_main
* DESCRIPTION: Create a socket, connecting on a hardcoded portnumber to
* a hardcoded server, and then create a simple Motif screen.
****************************************************************************
*/
void client_main(int argc, char **argv)
{
int rc, len, sock, newsock;
struct sockaddr_in addr;
struct hostent *host;
Display *dpy;
GC gc;
Window win;
XEvent xevt;
Widget toplevel, w;
XtInputId input_id;
XtAppContext app_cntxt;
Arg args[10];
Font fid;
XTextItem xtext;
int ac;
int blackColor, whiteColor;
int posx, posy, width, height;
char strBuf[32];
(void)fprintf(stderr, "Client to %s...\n", SERVER_NAME);
host = gethostbyname(SERVER_NAME);
if (host == (struct hostent*)NULL)
{
perror("gethostbyname");
exit(1);
}
(void)fprintf(stderr, "creating socket...\n");
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
perror("socket");
exit(1);
}
(void)memset((void *)&addr, '\0', sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NUM);
(void)memcpy((void *)&addr.sin_addr, (void *)host->h_addr,
(size_t)host->h_length);
(void)fprintf(stderr, "connecting...\n");
rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (rc == -1)
{
perror("connect");
exit(1);
}
toplevel = XtVaAppInitialize(
&app_cntxt, /* Application context */
"XHello", /* Application class */
NULL, 0, /* command line option list */
&argc, argv, /* command line args */
NULL, /* for missing app-defaults file */
NULL); /* terminate varargs list */
ac = 0;
XtSetArg(args[ac], XtNheight, 50);
ac++;
XtSetArg(args[ac], XtNwidth, 100);
ac++;
XtSetValues(toplevel, args, ac);
w = XtVaCreateManagedWidget(
"hello", /* arbitrary widget name */
simpleWidgetClass, /* widget class from Label.h */
toplevel, /* parent widget */
NULL); /* terminate varargs list */
XtAddCallback(w, XtNcallback, usr_callback, "Test");
XtAddEventHandler(w, ButtonPressMask, FALSE, usr_hndlr, &sock);
/*
* Create windows for widgets and map them.
*/
XtRealizeWidget(toplevel);
/*
* Loop for events.
*/
/* This is the code that should work - it works fine on all variants of
* UNIX - solaris, OSF/1, HP-UX, Linux, AIX, ULTRIX etc
*/
input_id = XtAppAddInput(app_cntxt, sock,
(XtPointer)(XtInputReadMask|XtInputExceptMask),
(XtInputCallbackProc)admin_server_input,
NULL);
XtAppMainLoop(app_cntxt);
/* The second parameter of XtAppAddInput pecifies the source file
* descriptor on a UNIX-based system
* or other operating system dependent device specification.
* If above code can`t work, following code can be a solution.
*/
//MainLoop(app_cntxt, toplevel, sock);
return;
}
/***************************************************************************
**
* FUNCTION: MainLoop
* DESCRIPTION: Implementation of an X mainloop function.
* It attempts to select on the server socket and the X socket,
* processing whichever has input.
****************************************************************************
*/
void MainLoop(XtAppContext app_cntxt, Widget toplevel, int sock)
{
int xfd, rc;
fd_set readfds;
Display *dsply;
dsply = XtDisplay(toplevel);
xfd = ConnectionNumber(dsply);
#ifdef USE_SELECT
for (;;)
{
/* In desperation, force all buffered X events out. */
XFlush(dsply);
fsync(xfd);
XSync(dsply, False);
/* Now try select'ing for any input on the client/server socket and
* on the connection to the X-server. */
FD_ZERO(&readfds);
FD_SET (sock, &readfds);
FD_SET (xfd, &readfds);
(void)fprintf(stderr, "selecting on %d and %d\n", sock, xfd);
rc = select(FD_SETSIZE, &readfds, NULL, NULL, (struct timeval *)NULL);
if (rc == -1)
{
perror("select");
exit(1);
}
/* If there's something on the X-socket, process it. */
if (FD_ISSET(xfd, &readfds) != 0)
{
process_events(app_cntxt);
continue;
}
/* If there's something on the from our socket, process it. */
if (FD_ISSET(sock, &readfds) != 0)
{
read_sock(sock);
continue;
}
}
#else /* Don't worry about the socket - just process X-requests. */
for (;;)
{
process_events(app_cntxt);
}
#endif /* USE_SELECT */
return;
}
/***************************************************************************
**
* FUNCTION: process_events
* DESCRIPTION: Loop, getting all events and dispatching them.
* This uses AppPending, as a way of seeing if there is any
* AlternateInput events coming in...
****************************************************************************
*/
void process_events(XtAppContext app_cntxt)
{
XtInputMask input_mask;
XEvent event;
while ((input_mask = XtAppPending(app_cntxt)) != 0)
{
if (input_mask == XtIMXEvent)
(void)fprintf(stderr, "XEvent\n");
if (input_mask == XtIMAlternateInput)
(void)fprintf(stderr, "AlternateInput\n");
XtAppNextEvent(app_cntxt, &event);
XtDispatchEvent(&event);
}
return;
}
/***************************************************************************
**
* FUNCTION: admin_server_input
* DESCRIPTION: XtInput callback - not used here unless XtAddInput is used.
****************************************************************************
*/
void admin_server_input(XtPointer client, int *fd, XtInputId *id)
{
(void)fprintf(stderr, "Read from socket ...\n");
read_sock(*fd);
return;
}
/***************************************************************************
**
* FUNCTION: read_sock
* DESCRIPTION: Receive a message from the server
****************************************************************************
*/
void read_sock(int sock)
{
int rc;
char buf[BUFSIZ];
rc = recv(sock, buf, sizeof(buf), 0);
if (rc == -1)
perror("recv");
buf[rc] = '\0';
(void)fprintf(stderr, "Recving '%s'\n", buf);
return;
}
阅读(1277) | 评论(0) | 转发(0) |