xref: /petsc/src/sys/classes/viewer/impls/socket/send.c (revision 1d27aa22b2f6148b2c4e3f06a75e0638d6493e09)
1 #include <petscsys.h>
2 
3 #if defined(PETSC_NEEDS_UTYPE_TYPEDEFS)
4 /* Some systems have inconsistent include files that use but do not
5    ensure that the following definitions are made */
6 typedef unsigned char  u_char;
7 typedef unsigned short u_short;
8 typedef unsigned short ushort;
9 typedef unsigned int   u_int;
10 typedef unsigned long  u_long;
11 #endif
12 
13 #include <errno.h>
14 #include <ctype.h>
15 #if defined(PETSC_HAVE_MACHINE_ENDIAN_H)
16   #include <machine/endian.h>
17 #endif
18 #if defined(PETSC_HAVE_UNISTD_H)
19   #include <unistd.h>
20 #endif
21 #if defined(PETSC_HAVE_SYS_SOCKET_H)
22   #include <sys/socket.h>
23 #endif
24 #if defined(PETSC_HAVE_SYS_WAIT_H)
25   #include <sys/wait.h>
26 #endif
27 #if defined(PETSC_HAVE_NETINET_IN_H)
28   #include <netinet/in.h>
29 #endif
30 #if defined(PETSC_HAVE_NETDB_H)
31   #include <netdb.h>
32 #endif
33 #if defined(PETSC_HAVE_FCNTL_H)
34   #include <fcntl.h>
35 #endif
36 #if defined(PETSC_HAVE_IO_H)
37   #include <io.h>
38 #endif
39 #if defined(PETSC_HAVE_WINSOCK2_H)
40   #include <Winsock2.h>
41 #endif
42 #include <sys/stat.h>
43 #include <../src/sys/classes/viewer/impls/socket/socket.h>
44 
45 #if defined(PETSC_NEED_CLOSE_PROTO)
46 PETSC_EXTERN int close(int);
47 #endif
48 #if defined(PETSC_NEED_SOCKET_PROTO)
49 PETSC_EXTERN int socket(int, int, int);
50 #endif
51 #if defined(PETSC_NEED_SLEEP_PROTO)
52 PETSC_EXTERN int sleep(unsigned);
53 #endif
54 #if defined(PETSC_NEED_CONNECT_PROTO)
55 PETSC_EXTERN int connect(int, struct sockaddr *, int);
56 #endif
57 
58 static PetscErrorCode PetscViewerDestroy_Socket(PetscViewer viewer)
59 {
60   PetscViewer_Socket *vmatlab = (PetscViewer_Socket *)viewer->data;
61 
62   PetscFunctionBegin;
63   if (vmatlab->port) {
64     int ierr;
65 
66 #if defined(PETSC_HAVE_CLOSESOCKET)
67     ierr = closesocket(vmatlab->port);
68 #else
69     ierr = close(vmatlab->port);
70 #endif
71     PetscCheck(!ierr, PETSC_COMM_SELF, PETSC_ERR_SYS, "System error closing socket");
72   }
73   PetscCall(PetscFree(vmatlab));
74   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinarySetSkipHeader_C", NULL));
75   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinaryGetSkipHeader_C", NULL));
76   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinaryGetFlowControl_C", NULL));
77   PetscFunctionReturn(PETSC_SUCCESS);
78 }
79 
80 /*@C
81   PetscOpenSocket - handles connected to an open port where someone is waiting.
82 
83   Input Parameters:
84 + hostname - for example www.mcs.anl.gov
85 - portnum  - for example 80
86 
87   Output Parameter:
88 . t - the socket number
89 
90   Notes:
91   Use close() to close the socket connection
92 
93   Use read() or `PetscHTTPRequest()` to read from the socket
94 
95   Level: advanced
96 
97 .seealso: `PetscSocketListen()`, `PetscSocketEstablish()`, `PetscHTTPRequest()`, `PetscHTTPSConnect()`
98 @*/
99 PetscErrorCode PetscOpenSocket(const char hostname[], int portnum, int *t)
100 {
101   struct sockaddr_in sa;
102   struct hostent    *hp;
103   int                s      = 0;
104   PetscBool          flg    = PETSC_TRUE;
105   static int         refcnt = 0;
106 
107   PetscFunctionBegin;
108   if (!(hp = gethostbyname(hostname))) {
109     perror("SEND: error gethostbyname: ");
110     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error open connection to %s", hostname);
111   }
112   PetscCall(PetscMemzero(&sa, sizeof(sa)));
113   PetscCall(PetscMemcpy(&sa.sin_addr, hp->h_addr_list[0], hp->h_length));
114 
115   sa.sin_family = hp->h_addrtype;
116   sa.sin_port   = htons((u_short)portnum);
117   while (flg) {
118     if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
119       perror("SEND: error socket");
120       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
121     }
122     if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
123 #if defined(PETSC_HAVE_WSAGETLASTERROR)
124       ierr = WSAGetLastError();
125       if (ierr == WSAEADDRINUSE) (*PetscErrorPrintf)("SEND: address is in use\n");
126       else if (ierr == WSAEALREADY) (*PetscErrorPrintf)("SEND: socket is non-blocking \n");
127       else if (ierr == WSAEISCONN) {
128         (*PetscErrorPrintf)("SEND: socket already connected\n");
129         Sleep((unsigned)1);
130       } else if (ierr == WSAECONNREFUSED) {
131         /* (*PetscErrorPrintf)("SEND: forcefully rejected\n"); */
132         Sleep((unsigned)1);
133       } else {
134         perror(NULL);
135         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
136       }
137 #else
138       if (errno == EADDRINUSE) {
139         PetscErrorCode ierr = (*PetscErrorPrintf)("SEND: address is in use\n");
140         (void)ierr;
141       } else if (errno == EALREADY) {
142         PetscErrorCode ierr = (*PetscErrorPrintf)("SEND: socket is non-blocking \n");
143         (void)ierr;
144       } else if (errno == EISCONN) {
145         PetscErrorCode ierr = (*PetscErrorPrintf)("SEND: socket already connected\n");
146         (void)ierr;
147         sleep((unsigned)1);
148       } else if (errno == ECONNREFUSED) {
149         refcnt++;
150         PetscCheck(refcnt <= 5, PETSC_COMM_SELF, PETSC_ERR_SYS, "Connection refused by remote host %s port %d", hostname, portnum);
151         PetscCall(PetscInfo(NULL, "Connection refused in attaching socket, trying again\n"));
152         sleep((unsigned)1);
153       } else {
154         perror(NULL);
155         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
156       }
157 #endif
158       flg = PETSC_TRUE;
159 #if defined(PETSC_HAVE_CLOSESOCKET)
160       closesocket(s);
161 #else
162       close(s);
163 #endif
164     } else flg = PETSC_FALSE;
165   }
166   *t = s;
167   PetscFunctionReturn(PETSC_SUCCESS);
168 }
169 
170 /*
171    PetscSocketEstablish - starts a listener on a socket
172 
173    Input Parameter:
174 .    portnumber - the port to wait at
175 
176    Output Parameter:
177 .     ss - the socket to be used with `PetscSocketListen()`
178 
179     Level: advanced
180 
181 .seealso: `PetscSocketListen()`, `PetscOpenSocket()`
182 */
183 static PetscErrorCode PetscSocketEstablish(int portnum, int *ss)
184 {
185   static size_t      MAXHOSTNAME = 100;
186   char               myname[MAXHOSTNAME + 1];
187   int                s;
188   struct sockaddr_in sa;
189   struct hostent    *hp;
190 
191   PetscFunctionBegin;
192   PetscCall(PetscGetHostName(myname, sizeof(myname)));
193 
194   PetscCall(PetscMemzero(&sa, sizeof(struct sockaddr_in)));
195 
196   hp = gethostbyname(myname);
197   PetscCheck(hp, PETSC_COMM_SELF, PETSC_ERR_SYS, "Unable to get hostent information from system");
198 
199   sa.sin_family = hp->h_addrtype;
200   sa.sin_port   = htons((u_short)portnum);
201 
202   PetscCheck((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "Error running socket() command");
203 #if defined(PETSC_HAVE_SO_REUSEADDR)
204   {
205     int optval = 1; /* Turn on the option */
206     int ret    = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
207     PetscCheck(!ret, PETSC_COMM_SELF, PETSC_ERR_LIB, "setsockopt() failed with error code %d", ret);
208   }
209 #endif
210 
211   while (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
212 #if defined(PETSC_HAVE_WSAGETLASTERROR)
213     ierr = WSAGetLastError();
214     if (ierr != WSAEADDRINUSE) {
215 #else
216     if (errno != EADDRINUSE) {
217 #endif
218       close(s);
219       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "Error from bind()");
220     }
221   }
222   listen(s, 0);
223   *ss = s;
224   PetscFunctionReturn(PETSC_SUCCESS);
225 }
226 
227 /*
228    PetscSocketListen - Listens at a socket created with `PetscSocketEstablish()`
229 
230    Input Parameter:
231 .    listenport - obtained with `PetscSocketEstablish()`
232 
233    Output Parameter:
234 .     t - pass this to read() to read what is passed to this connection
235 
236     Level: advanced
237 
238 .seealso: `PetscSocketEstablish()`
239 */
240 static PetscErrorCode PetscSocketListen(int listenport, int *t)
241 {
242   struct sockaddr_in isa;
243 #if defined(PETSC_HAVE_ACCEPT_SIZE_T)
244   size_t i;
245 #else
246   int i;
247 #endif
248 
249   PetscFunctionBegin;
250   /* wait for someone to try to connect */
251   i = sizeof(struct sockaddr_in);
252   PetscCheck((*t = accept(listenport, (struct sockaddr *)&isa, (socklen_t *)&i)) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "error from accept()");
253   PetscFunctionReturn(PETSC_SUCCESS);
254 }
255 
256 // "Unknown section 'Environmental Variables'"
257 // PetscClangLinter pragma disable: -fdoc-section-header-unknown
258 /*@C
259   PetscViewerSocketOpen - Opens a connection to a MATLAB or other socket based server.
260 
261   Collective
262 
263   Input Parameters:
264 + comm    - the MPI communicator
265 . machine - the machine the server is running on, use `NULL` for the local machine, use "server" to passively wait for
266              a connection from elsewhere
267 - port    - the port to connect to, use `PETSC_DEFAULT` for the default
268 
269   Output Parameter:
270 . lab - a context to use when communicating with the server
271 
272   Options Database Keys:
273    For use with  `PETSC_VIEWER_SOCKET_WORLD`, `PETSC_VIEWER_SOCKET_SELF`,
274    `PETSC_VIEWER_SOCKET_()` or if
275     `NULL` is passed for machine or PETSC_DEFAULT is passed for port
276 + -viewer_socket_machine <machine> - the machine where the socket is available
277 - -viewer_socket_port <port>       - the socket to connect to
278 
279   Environmental variables:
280 +   `PETSC_VIEWER_SOCKET_MACHINE` - machine name
281 -   `PETSC_VIEWER_SOCKET_PORT` - portnumber
282 
283   Level: intermediate
284 
285   Notes:
286   Most users should employ the following commands to access the
287   MATLAB `PetscViewer`
288 .vb
289 
290     PetscViewerSocketOpen(MPI_Comm comm, char *machine,int port,PetscViewer &viewer)
291     MatView(Mat matrix,PetscViewer viewer)
292 .ve
293   or
294 .vb
295     PetscViewerSocketOpen(MPI_Comm comm,char *machine,int port,PetscViewer &viewer)
296     VecView(Vec vector,PetscViewer viewer)
297 .ve
298 
299   Currently the only socket client available is MATLAB, PETSc must be configured with --with-matlab for this client. See
300   src/dm/tests/ex12.c and ex12.m for an example of usage.
301 
302   The socket viewer is in some sense a subclass of the binary viewer, to read and write to the socket
303   use `PetscViewerBinaryRead()`, `PetscViewerBinaryWrite()`, `PetscViewerBinarWriteStringArray()`, `PetscViewerBinaryGetDescriptor()`.
304 
305   Use this for communicating with an interactive MATLAB session, see `PETSC_VIEWER_MATLAB_()` for writing output to a
306   .mat file. Use `PetscMatlabEngineCreate()` or `PETSC_MATLAB_ENGINE_()`, `PETSC_MATLAB_ENGINE_SELF`, or `PETSC_MATLAB_ENGINE_WORLD`
307   for communicating with a MATLAB Engine
308 
309 .seealso: [](sec_viewers), `PETSCVIEWERBINARY`, `PETSCVIEWERSOCKET`, `MatView()`, `VecView()`, `PetscViewerDestroy()`, `PetscViewerCreate()`, `PetscViewerSetType()`,
310           `PetscViewerSocketSetConnection()`, `PETSC_VIEWER_SOCKET_`, `PETSC_VIEWER_SOCKET_WORLD`,
311           `PETSC_VIEWER_SOCKET_SELF`, `PetscViewerBinaryWrite()`, `PetscViewerBinaryRead()`, `PetscViewerBinaryWriteStringArray()`,
312           `PetscBinaryViewerGetDescriptor()`, `PetscMatlabEngineCreate()`
313 @*/
314 PetscErrorCode PetscViewerSocketOpen(MPI_Comm comm, const char machine[], int port, PetscViewer *lab)
315 {
316   PetscFunctionBegin;
317   PetscCall(PetscViewerCreate(comm, lab));
318   PetscCall(PetscViewerSetType(*lab, PETSCVIEWERSOCKET));
319   PetscCall(PetscViewerSocketSetConnection(*lab, machine, port));
320   PetscFunctionReturn(PETSC_SUCCESS);
321 }
322 
323 static PetscErrorCode PetscViewerSetFromOptions_Socket(PetscViewer v, PetscOptionItems *PetscOptionsObject)
324 {
325   PetscInt  def = -1;
326   char      sdef[256];
327   PetscBool tflg;
328 
329   PetscFunctionBegin;
330   /*
331        These options are not processed here, they are processed in PetscViewerSocketSetConnection(), they
332     are listed here for the GUI to display
333   */
334   PetscOptionsHeadBegin(PetscOptionsObject, "Socket PetscViewer Options");
335   PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_PORT", sdef, 16, &tflg));
336   if (tflg) {
337     PetscCall(PetscOptionsStringToInt(sdef, &def));
338   } else def = PETSCSOCKETDEFAULTPORT;
339   PetscCall(PetscOptionsInt("-viewer_socket_port", "Port number to use for socket", "PetscViewerSocketSetConnection", def, NULL, NULL));
340 
341   PetscCall(PetscOptionsString("-viewer_socket_machine", "Machine to use for socket", "PetscViewerSocketSetConnection", sdef, NULL, sizeof(sdef), NULL));
342   PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_MACHINE", sdef, sizeof(sdef), &tflg));
343   if (!tflg) PetscCall(PetscGetHostName(sdef, sizeof(sdef)));
344   PetscOptionsHeadEnd();
345   PetscFunctionReturn(PETSC_SUCCESS);
346 }
347 
348 static PetscErrorCode PetscViewerBinaryGetSkipHeader_Socket(PetscViewer viewer, PetscBool *skip)
349 {
350   PetscViewer_Socket *vsocket = (PetscViewer_Socket *)viewer->data;
351 
352   PetscFunctionBegin;
353   *skip = vsocket->skipheader;
354   PetscFunctionReturn(PETSC_SUCCESS);
355 }
356 
357 static PetscErrorCode PetscViewerBinarySetSkipHeader_Socket(PetscViewer viewer, PetscBool skip)
358 {
359   PetscViewer_Socket *vsocket = (PetscViewer_Socket *)viewer->data;
360 
361   PetscFunctionBegin;
362   vsocket->skipheader = skip;
363   PetscFunctionReturn(PETSC_SUCCESS);
364 }
365 
366 PETSC_INTERN PetscErrorCode PetscViewerBinaryGetFlowControl_Binary(PetscViewer, PetscInt *);
367 
368 /*MC
369    PETSCVIEWERSOCKET - A viewer that writes to a Unix socket
370 
371   Level: beginner
372 
373 .seealso: [](sec_viewers), `PETSC_VIEWERBINARY`, `PetscViewerSocketOpen()`, `PetscViewerDrawOpen()`, `PETSC_VIEWER_DRAW_()`, `PETSC_VIEWER_DRAW_SELF`, `PETSC_VIEWER_DRAW_WORLD`,
374           `PetscViewerCreate()`, `PetscViewerASCIIOpen()`, `PetscViewerBinaryOpen()`, `PETSCVIEWERBINARY`, `PETSCVIEWERDRAW`,
375           `PetscViewerMatlabOpen()`, `VecView()`, `DMView()`, `PetscViewerMatlabPutArray()`, `PETSCVIEWERASCII`, `PETSCVIEWERMATLAB`,
376           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
377 M*/
378 
379 PETSC_EXTERN PetscErrorCode PetscViewerCreate_Socket(PetscViewer v)
380 {
381   PetscViewer_Socket *vmatlab;
382 
383   PetscFunctionBegin;
384   PetscCall(PetscNew(&vmatlab));
385   vmatlab->port          = 0;
386   vmatlab->flowcontrol   = 256; /* same default as in PetscViewerCreate_Binary() */
387   v->data                = (void *)vmatlab;
388   v->ops->destroy        = PetscViewerDestroy_Socket;
389   v->ops->flush          = NULL;
390   v->ops->setfromoptions = PetscViewerSetFromOptions_Socket;
391 
392   /* lie and say this is a binary viewer; then all the XXXView_Binary() methods will work correctly on it */
393   PetscCall(PetscObjectChangeTypeName((PetscObject)v, PETSCVIEWERBINARY));
394   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinarySetSkipHeader_C", PetscViewerBinarySetSkipHeader_Socket));
395   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinaryGetSkipHeader_C", PetscViewerBinaryGetSkipHeader_Socket));
396   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinaryGetFlowControl_C", PetscViewerBinaryGetFlowControl_Binary));
397 
398   PetscFunctionReturn(PETSC_SUCCESS);
399 }
400 
401 /*@C
402   PetscViewerSocketSetConnection - Sets the machine and port that a PETSc socket
403   viewer is to use
404 
405   Logically Collective
406 
407   Input Parameters:
408 + v       - viewer to connect
409 . machine - host to connect to, use `NULL` for the local machine,use "server" to passively wait for
410              a connection from elsewhere
411 - port    - the port on the machine one is connecting to, use `PETSC_DEFAULT` for default
412 
413   Level: advanced
414 
415 .seealso: [](sec_viewers), `PETSCVIEWERMATLAB`, `PETSCVIEWERSOCKET`, `PetscViewerSocketOpen()`
416 @*/
417 PetscErrorCode PetscViewerSocketSetConnection(PetscViewer v, const char machine[], int port)
418 {
419   PetscMPIInt         rank;
420   char                mach[256];
421   PetscBool           tflg;
422   PetscViewer_Socket *vmatlab;
423 
424   PetscFunctionBegin;
425   PetscValidHeaderSpecific(v, PETSC_VIEWER_CLASSID, 1);
426   if (machine) PetscAssertPointer(machine, 2);
427   vmatlab = (PetscViewer_Socket *)v->data;
428   /* PetscValidLogicalCollectiveInt(v,port,3); not a PetscInt */
429   if (port <= 0) {
430     char portn[16];
431     PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_PORT", portn, 16, &tflg));
432     if (tflg) {
433       PetscInt pport;
434       PetscCall(PetscOptionsStringToInt(portn, &pport));
435       port = (int)pport;
436     } else port = PETSCSOCKETDEFAULTPORT;
437   }
438   if (!machine) {
439     PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_MACHINE", mach, sizeof(mach), &tflg));
440     if (!tflg) PetscCall(PetscGetHostName(mach, sizeof(mach)));
441   } else {
442     PetscCall(PetscStrncpy(mach, machine, sizeof(mach)));
443   }
444 
445   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)v), &rank));
446   if (rank == 0) {
447     PetscCall(PetscStrcmp(mach, "server", &tflg));
448     if (tflg) {
449       int listenport = 0;
450 
451       PetscCall(PetscInfo(v, "Waiting for connection from socket process on port %d\n", port));
452       PetscCall(PetscSocketEstablish(port, &listenport));
453       PetscCall(PetscSocketListen(listenport, &vmatlab->port));
454       close(listenport);
455     } else {
456       PetscCall(PetscInfo(v, "Connecting to socket process on port %d machine %s\n", port, mach));
457       PetscCall(PetscOpenSocket(mach, port, &vmatlab->port));
458     }
459   }
460   PetscFunctionReturn(PETSC_SUCCESS);
461 }
462 
463 /*
464     The variable Petsc_Viewer_Socket_keyval is used to indicate an MPI attribute that
465   is attached to a communicator, in this case the attribute is a PetscViewer.
466 */
467 PetscMPIInt Petsc_Viewer_Socket_keyval = MPI_KEYVAL_INVALID;
468 
469 /*@C
470      PETSC_VIEWER_SOCKET_ - Creates a socket viewer shared by all processors in a communicator.
471 
472      Collective
473 
474      Input Parameter:
475 .    comm - the MPI communicator to share the  `PETSCVIEWERSOCKET` `PetscViewer`
476 
477      Level: intermediate
478 
479    Options Database Keys:
480    For use with the default `PETSC_VIEWER_SOCKET_WORLD` or if
481     `NULL` is passed for machine or `PETSC_DEFAULT` is passed for port
482 +    -viewer_socket_machine <machine> - machine to connect to
483 -    -viewer_socket_port <port> - port to connect to
484 
485    Environmental variables:
486 +   `PETSC_VIEWER_SOCKET_PORT` - portnumber
487 -   `PETSC_VIEWER_SOCKET_MACHINE` - machine name
488 
489      Notes:
490      This object is destroyed in `PetscFinalize()`, `PetscViewerDestroy()` should never be called on it
491 
492      Unlike almost all other PETSc routines, `PETSC_VIEWER_SOCKET_()` does not return
493      an error code, it returns NULL if it fails. The  `PETSCVIEWERSOCKET`  `PetscViewer` is usually used in the form
494 $       XXXView(XXX object, PETSC_VIEWER_SOCKET_(comm));
495 
496      Currently the only socket client available is MATLAB. See
497      src/dm/tests/ex12.c and ex12.m for an example of usage.
498 
499      Connects to a waiting socket and stays connected until `PetscViewerDestroy()` is called.
500 
501      Use this for communicating with an interactive MATLAB session, see `PETSC_VIEWER_MATLAB_()` for writing output to a
502      .mat file. Use `PetscMatlabEngineCreate()` or `PETSC_MATLAB_ENGINE_()`, `PETSC_MATLAB_ENGINE_SELF`, or `PETSC_MATLAB_ENGINE_WORLD`
503      for communicating with a MATLAB Engine
504 
505 .seealso: [](sec_viewers), `PETSCVIEWERMATLAB`, `PETSCVIEWERSOCKET`, `PETSC_VIEWER_SOCKET_WORLD`, `PETSC_VIEWER_SOCKET_SELF`, `PetscViewerSocketOpen()`, `PetscViewerCreate()`,
506           `PetscViewerSocketSetConnection()`, `PetscViewerDestroy()`, `PETSC_VIEWER_SOCKET_()`, `PetscViewerBinaryWrite()`, `PetscViewerBinaryRead()`,
507           `PetscViewerBinaryWriteStringArray()`, `PetscViewerBinaryGetDescriptor()`, `PETSC_VIEWER_MATLAB_()`
508 @*/
509 PetscViewer PETSC_VIEWER_SOCKET_(MPI_Comm comm)
510 {
511   PetscErrorCode ierr;
512   PetscMPIInt    mpi_ierr;
513   PetscBool      flg;
514   PetscViewer    viewer;
515   MPI_Comm       ncomm;
516 
517   PetscFunctionBegin;
518   ierr = PetscCommDuplicate(comm, &ncomm, NULL);
519   if (ierr) {
520     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
521     PetscFunctionReturn(NULL);
522   }
523   if (Petsc_Viewer_Socket_keyval == MPI_KEYVAL_INVALID) {
524     mpi_ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Viewer_Socket_keyval, NULL);
525     if (mpi_ierr) {
526       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
527       PetscFunctionReturn(NULL);
528     }
529   }
530   mpi_ierr = MPI_Comm_get_attr(ncomm, Petsc_Viewer_Socket_keyval, (void **)&viewer, (int *)&flg);
531   if (mpi_ierr) {
532     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
533     PetscFunctionReturn(NULL);
534   }
535   if (!flg) { /* PetscViewer not yet created */
536     ierr                              = PetscViewerSocketOpen(ncomm, NULL, 0, &viewer);
537     ((PetscObject)viewer)->persistent = PETSC_TRUE;
538     if (ierr) {
539       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
540       PetscFunctionReturn(NULL);
541     }
542     ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
543     if (ierr) {
544       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
545       PetscFunctionReturn(NULL);
546     }
547     mpi_ierr = MPI_Comm_set_attr(ncomm, Petsc_Viewer_Socket_keyval, (void *)viewer);
548     if (mpi_ierr) {
549       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
550       PetscFunctionReturn(NULL);
551     }
552   }
553   ierr = PetscCommDestroy(&ncomm);
554   if (ierr) {
555     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
556     PetscFunctionReturn(NULL);
557   }
558   PetscFunctionReturn(viewer);
559 }
560