00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * RCS INFORMATION: 00011 * 00012 * $RCSfile: VMDCollab.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.11 $ $Date: 2019年01月17日 21:21:02 $ 00015 * 00016 ***************************************************************************/ 00017 00018 #include "VMDCollab.h" 00019 #include "WKFThreads.h" 00020 #include "vmdsock.h" 00021 #include "Inform.h" 00022 #include "utilities.h" 00023 #include "TextEvent.h" 00024 00025 #if defined(VMDTKCON) 00026 #include "vmdconsole.h" 00027 #endif 00028 00029 #include <limits.h> 00030 #include <errno.h> 00031 00032 // collab messages will be the following number of bytes 00033 static const int VMDCOLLAB_MSGSIZE = 256; 00034 00035 00036 #if ( INT_MAX == 2147483647 ) 00037 typedef int int32; 00038 #else 00039 typedef short int32; 00040 #endif 00041 00042 static int32 imd_readn(void *s, char *ptr, int32 n) { 00043 int32 nleft; 00044 int32 nread; 00045 00046 nleft = n; 00047 while (nleft > 0) { 00048 if ((nread = vmdsock_read(s, ptr, nleft)) < 0) { 00049 if (errno == EINTR) 00050 nread = 0; /* and call read() again */ 00051 else 00052 return -1; 00053 } else if (nread == 0) 00054 break; /* EOF */ 00055 nleft -= nread; 00056 ptr += nread; 00057 } 00058 return n-nleft; 00059 } 00060 00061 00062 static int32 imd_writen(void *s, const char *ptr, int32 n) { 00063 int32 nleft; 00064 int32 nwritten; 00065 00066 nleft = n; 00067 while (nleft > 0) { 00068 if ((nwritten = vmdsock_write(s, ptr, nleft)) <= 0) { 00069 if (errno == EINTR) 00070 nwritten = 0; 00071 else 00072 return -1; 00073 } 00074 nleft -= nwritten; 00075 ptr += nwritten; } 00076 return n; 00077 } 00078 00079 VMDCollab::VMDCollab(VMDApp *app) : UIObject(app) { 00080 clientsock = NULL; 00081 serversock = NULL; 00082 eval_in_progress = FALSE; 00083 #if defined(VMDTKCON) 00084 cmdbufstr = new Inform("",VMDCON_ALWAYS); 00085 #else 00086 cmdbufstr = new Inform(""); 00087 #endif 00088 for (int i=0; i<Command::TOTAL; i++) command_wanted(i); 00089 } 00090 00091 00092 VMDCollab::~VMDCollab() { 00093 stopserver(); 00094 delete cmdbufstr; 00095 } 00096 00097 00098 // this is a static method that will be created in a new child thread 00099 void *VMDCollab::serverproc(void *serversock) { 00100 ResizeArray<void *>clients; 00101 char buf[VMDCOLLAB_MSGSIZE]; 00102 int i, j; 00103 00104 while (1) { 00105 // if we have no clients, hang until someone connects 00106 // otherwise, just check for pending connections 00107 if (vmdsock_selread(serversock, 0) > 0) { 00108 msgInfo << "serversock became readable" << sendmsg; 00109 void *clientsock = vmdsock_accept(serversock); 00110 if (clientsock) { 00111 msgInfo << "VMDCollab accepting connection" << sendmsg; 00112 clients.append(clientsock); 00113 } 00114 } else if (vmdsock_selwrite(serversock, 0)) { 00115 msgInfo << "serversock became writable; exiting..." << sendmsg; 00116 break; 00117 } 00118 00119 // Loop through one socket at a time. If incoming data is found, 00120 // drain it before moving on, on the assumption that we only want 00121 // commands from one VMD at a time to be propagated to the other 00122 // clients. 00123 for (i=0; i<clients.num(); i++) { 00124 void *client = clients[i]; 00125 while (vmdsock_selread(client, 0) > 0) { 00126 memset(buf, 0, VMDCOLLAB_MSGSIZE); 00127 if (imd_readn(client, buf, VMDCOLLAB_MSGSIZE) != VMDCOLLAB_MSGSIZE) { 00128 msgInfo << "client sent incomplete message, shutting it down" 00129 << sendmsg; 00130 vmdsock_shutdown(client); 00131 vmdsock_destroy(client); 00132 clients.remove(clients.find(client)); 00133 break; 00134 } 00135 // send to all other clients 00136 for (j=0; j<clients.num(); j++) { 00137 void *dest = clients[j]; 00138 if (dest != client) { 00139 imd_writen(clients[j], buf, VMDCOLLAB_MSGSIZE); 00140 } 00141 } // loop over clients other than sender 00142 } // while client is readable 00143 } // loop over clients 00144 vmd_msleep(10); 00145 } 00146 00147 // if here, then the serversock got shut down, indicating that it's 00148 // time to die. 00149 msgInfo << "VMDCollab shutting down server" << sendmsg; 00150 for (i=0; i<clients.num(); i++) { 00151 void *client = clients[i]; 00152 strcpy(buf, "exit"); 00153 imd_writen(client, buf, VMDCOLLAB_MSGSIZE); 00154 vmdsock_shutdown(client); 00155 vmdsock_destroy(client); 00156 } 00157 vmdsock_destroy(serversock); 00158 return NULL; 00159 } 00160 00161 00162 int VMDCollab::startserver(int port) { 00163 if (serversock) { 00164 msgErr << "Already running a server on port " << port << sendmsg; 00165 return FALSE; 00166 } 00167 serversock = vmdsock_create(); 00168 if (!serversock) { 00169 msgErr << "Could not create socket." << sendmsg; 00170 return FALSE; 00171 } 00172 if (vmdsock_bind(serversock, port)) { 00173 msgErr << "Could not bind vmdcollab server to port " << port 00174 << sendmsg; 00175 vmdsock_destroy(serversock); 00176 return FALSE; 00177 } 00178 vmdsock_listen(serversock); 00179 00180 wkf_thread_t serverthread; 00181 if (wkf_thread_create(&serverthread, 00182 serverproc, // my thread routine 00183 serversock // context for thread 00184 )) { 00185 msgErr << "VMDCollab: unable to create server thread" << sendmsg; 00186 } else { 00187 msgInfo << "Starting VMDCollab bounce server." << sendmsg; 00188 } 00189 00190 return TRUE; 00191 } 00192 00193 00194 void VMDCollab::stopserver() { 00195 if (!serversock) return; 00196 vmdsock_shutdown(serversock); 00197 // don't destroy; let the server thread do that 00198 serversock = NULL; 00199 } 00200 00201 00202 int VMDCollab::connect(const char *host, int port) { 00203 if (clientsock) { 00204 msgErr << "Already connected to another vmdcollab server" << sendmsg; 00205 return FALSE; 00206 } 00207 if (!(clientsock = vmdsock_create())) { 00208 msgErr << "Could not create socket." << sendmsg; 00209 return FALSE; 00210 } 00211 int numTries = 3; 00212 for (int i=0; i<numTries; i++) { 00213 if (vmdsock_connect(clientsock, host, port)) { 00214 msgErr << "Could not connect to vmdcollab server at " << host << ":" << port << sendmsg; 00215 msgErr << "Error: " << strerror(errno) << sendmsg; 00216 } else { 00217 // success 00218 return TRUE; 00219 } 00220 // sleep for a second; maybe the server just hasn't started yet 00221 vmd_sleep(1); 00222 } 00223 // failed 00224 msgErr << "VMDCollab giving up after " << numTries << " seconds." << sendmsg; 00225 vmdsock_destroy(clientsock); 00226 clientsock = NULL; 00227 return FALSE; 00228 } 00229 00230 00231 void VMDCollab::disconnect() { 00232 if (!clientsock) return; 00233 vmdsock_shutdown(clientsock); 00234 vmdsock_destroy(clientsock); 00235 clientsock = NULL; 00236 } 00237 00238 00239 int VMDCollab::check_event() { 00240 if (!clientsock) return FALSE; 00241 eval_in_progress = TRUE; 00242 char buf[VMDCOLLAB_MSGSIZE]; 00243 while (vmdsock_selread(clientsock, 0) > 0) { 00244 if (imd_readn(clientsock, buf, VMDCOLLAB_MSGSIZE) != VMDCOLLAB_MSGSIZE) { 00245 vmdsock_shutdown(clientsock); 00246 vmdsock_destroy(clientsock); 00247 clientsock = NULL; 00248 break; 00249 } 00250 runcommand(new TclEvalEvent(buf)); 00251 } 00252 eval_in_progress = FALSE; 00253 return TRUE; 00254 } 00255 00256 00257 int VMDCollab::act_on_command(int, Command *cmd) { 00258 if (!clientsock) return FALSE; 00259 if (eval_in_progress) return FALSE; 00260 if (!cmd->has_text(cmdbufstr)) return TRUE; 00261 00262 const char *txtcmd = cmdbufstr->text(); 00263 int len = strlen(txtcmd); 00264 if (len >= VMDCOLLAB_MSGSIZE) { 00265 msgWarn << "VMDCollab: command too long: " << txtcmd << sendmsg; 00266 return FALSE; 00267 } 00268 00269 char buf[VMDCOLLAB_MSGSIZE]; 00270 strcpy(buf, txtcmd); 00271 cmdbufstr->reset(); 00272 00273 imd_writen(clientsock, buf, VMDCOLLAB_MSGSIZE); 00274 // give the server thread a chance to propagate events before continuing 00275 vmd_msleep(1); 00276 return TRUE; 00277 } 00278