Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
Inter-Process Communication by Sockets Simon Says Inter-Process Communication     Our project will be to create a set of processes that will do Simon Says using two different communication methods. The simon process will send commands to three other processes  which will then execute those commands. Typically the command will be passed as a string and each receiving processes will look at it to see if it can handle it or it must exec another process to accomplish the task.   0. Inter-process communication   For two processes to communicate with each other, they must both agree to it, and the operating system must provide some facilities for the inter-process communication (IPC). While some form of IPC is required for network programming, the use of IPC is by no means restricted to processes executing on different systems, communicating through a network of some form. Indeed, there are many instances where IPC can and should be used between processes on a single computer system. We can diagram two processes using IPC on a single computer system as figure 1:                                                                                                                                                           Figure 1     This figure shows the information between the two processes going through the kernel, although this is typical, it is not a requirement for IPC. To expand this to processes on different systems, using some form of networking between the two systems, we have picture shown in figure 2:                                                                                                                                                                 Figure 2     1. Pipes                                                                                                                                                           Figure 3 Unix originally just had the pipe as the IPC mechanism. The pipe is a convenient high level mechanism which is created with the pipe system call, e.g.:          int pipe(int filedes[2]);   pipe creates a pair of file descriptors, pointing to a pipe i-node (similar to a file i-node), and places them in the array pointed to by filedes; filedes[0] is for reading and filedes[1] is for writing. On success, zero is returned, on error, -1 is returned and errno is set appropriately.  For example:        #include      int fd[2], rval;      if ((rval = pipe(fd)) < 0) cout << " error creating pupe";   Once created the pipe can be used with read and write as though it is a file (can't use seek).   If a process reads from an empty pipe it blocks until some data is put into the pipe.  If a process fills a pipe it blocks until some bytes are removed from the pipe.  The read command will return with zero bytes when end of file is met, e.g. when the pipe has no writing processes left.    The pipe file-ids are passed to a child on forking to enable it to communicate with the parent.   An interesting fact is that stdin and stdout can be redirected to pipes this allows such things as child processes to execute programs capture their output and send back it back to parent. simple pipe example   #include #include #include #include   int main(void) {     int pid, p[2];                                                                                                                                                                                                                                                  char buffer2[] = "hello", buffer1[100], N = 6;        //don’t forget end of string character when counting size     if (pipe(p) < 0)                                                                                                                                                                                                                                                    {         cerr << "pipe error" << endl;         exit(1);         }     if ((pid = fork()) == 0)         {                                                                                                                                                                                                                                                               read(p[0], buffer1, N);                                                                                                                                                                                                                                         cout << "child received: " << buffer1 << endl;         }     if ( pid > 0)         {                                                                                                                                                                                                                                                               int status;         cout << "parent sending: " << buffer2 << endl;         write(p[1], buffer2, N);                                                                                                                                                                                                                                       wait(&status);                                                                                                                                                                                                                                                 }     return 0; }         2. Sockets A socket is defined as an endpoint for communication. A pair of processes communicating over a network employs a pair of sockets, one for each process. A socket is made up of an IP address concatenated with a port number. In general, sockets use a client-server architecture. The server waits for incoming client request by listening to a specified port. Once a request is received, the server accepts a connection from the client socket to complete the connection. Servers implementing specific services (such as telnet, ftp, and http) listen to well-known ports (a telnet server listens to port 23, an ftp server listens to port 21, and a web (or http) server listens to port 80). All ports below 1024 are considered well known; we can use them to implement standard services. Web Server (161.25.19.8)   Computer X (146.86.5.20)   When a client process initiates a request for a connection, it is assigned a port. This port is some arbitrary number greater than 1024. For example, if a client on computer X with IP address 146.86.5.20 wishes to establish a connection with a web server (which is listening on port 80) at address 161.25.19.8, host X may be assigned port 1625. The connection will consist of a pair of sockets: (146.86.5.20:1625) on host X, and (161.25.19.8:80) on the web server. This situation is illustrated in figure 3. The packets traveling between the hosts are delivered to the appropriate process, based on the destination port number.                 Useful network applications: a)       You can use the hostname and uname commands to display the name of a computer.  Try the following commands: hostname and uname –n b)      You can test the status of a network or a particular host on it by using the ping utility. For testing the connection of id415m05, type ping mhc . c)       The telnet program allow you to connect to a remote computer over a network. This is an example: telnet id415m03.cs.unb.ca . Type your userid and password to login to this computer. d)      Because Internet software deals with IP addresses and people prefer to use symbolic names, application software  translates symbolic names to equivalent IP addresses. You can use nslookup command to display the IP address of a host whose domain name is passed as a command line argument to it. for example Type nslookup www.mtholyoke.edu to find the IP address of the web server at MHC. A computer’s hostname may be used instead of its IP address e)       The name localhost  can used to address the machine a process is runnig on if a process is referring to a process on same machine it maybe referred to as localhost.   for further information see reference on examples below http://sage.mc.yu.edu/kbeen/teaching/networking/resources/sockets.html example server code   #include   #include #include #include #include   #define MAX_MSG 100 #define LINE_ARRAY_SIZE (MAX_MSG+1)   using namespace std;   int main() {   int listenSocket, connectSocket, i;   unsigned short int listenPort;   socklen_t clientAddressLength;   struct sockaddr_in clientAddress, serverAddress;   char line[LINE_ARRAY_SIZE];     cout << "Enter port number to listen on (between 1500 and 65000): ";   cin >> listenPort;     // Create socket for listening for client connection requests.   listenSocket = socket(AF_INET, SOCK_STREAM, 0);   if (listenSocket < 0) {     cerr << "cannot create listen socket";     exit(1);   }     // Bind listen socket to listen port.  First set various fields in   // the serverAddress structure, then call bind().   // htonl() and htons() convert long integers and short integers   // (respectively) from host byte order (on x86 this is Least   // Significant Byte first) to network byte order (Most Significant   // Byte first).   serverAddress.sin_family = AF_INET;   serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);   serverAddress.sin_port = htons(listenPort);     if (bind(listenSocket,            (struct sockaddr *) &serverAddress,            sizeof(serverAddress)) < 0) {     cerr << "cannot bind socket";     exit(1);   }     // Wait for connections from clients.   // This is a non-blocking call; i.e., it registers this program with   // the system as expecting connections on this socket, and then   // this thread of execution continues on.   listen(listenSocket, 5);     while (1) {     cout << "Waiting for TCP connection on port " << listenPort << " ...\n";       // Accept a connection with a client that is requesting one.  The     // accept() call is a blocking call; i.e., this thread of     // execution stops until a connection comes in.     // connectSocket is a new socket that the system provides,     // separate from listenSocket.  We *could* accept more     // connections on listenSocket, before connectSocket is closed,     // but this program doesn't do that.     clientAddressLength = sizeof(clientAddress);     connectSocket = accept(listenSocket,                            (struct sockaddr *) &clientAddress,                            &clientAddressLength);     if (connectSocket < 0) {       cerr << "cannot accept connection ";       exit(1);     }         // Show the IP address of the client.     // inet_ntoa() converts an IP address from binary form to the     // standard "numbers and dots" notation.     cout << "  connected to " << inet_ntoa(clientAddress.sin_addr);       // Show the client's port number.     // ntohs() converts a short int from network byte order (which is     // Most Significant Byte first) to host byte order (which on x86,     // for example, is Least Significant Byte first).     cout << ":" << ntohs(clientAddress.sin_port) << "\n";       // Read lines from socket, using recv(), storing them in the line     // array.  If no messages are currently available, recv() blocks     // until one arrives.     // First set line to all zeroes, so we'll know where the end of     // the string is.     memset(line, 0x0, LINE_ARRAY_SIZE);     while (recv(connectSocket, line, MAX_MSG, 0) > 0) {       cout << "  --  " << line << "\n";         // Convert line to upper case.       for (i = 0; line[i] != '\0'; i++)         line[i] = toupper(line[i]);         // Send converted line back to client.       if (send(connectSocket, line, strlen(line) + 1, 0) < 0)         cerr << "Error: cannot send modified data";         memset(line, 0x0, LINE_ARRAY_SIZE);  // set line to all zeroes     }   } }     example client code #include #include #include #include   #define MAX_LINE 100 #define LINE_ARRAY_SIZE (MAX_LINE+1)   using namespace std;   int main() {   int socketDescriptor;   unsigned short int serverPort;   struct sockaddr_in serverAddress;   struct hostent *hostInfo;   char buf[LINE_ARRAY_SIZE], c;     cout << "Enter server host name or IP address: ";   cin.get(buf, MAX_LINE, '\n');     // gethostbyname() takes a host name or ip address in "numbers and   // dots" notation, and returns a pointer to a hostent structure,   // which we'll need later.  It's not important for us what this   // structure is actually composed of.   hostInfo = gethostbyname(buf);   if (hostInfo == NULL) {     cout << "problem interpreting host: " << buf << "\n";     exit(1);   }     cout << "Enter server port number: ";   cin >> serverPort;   cin.get(c); // dispose of the newline     // Create a socket.  "AF_INET" means it will use the IPv4 protocol.   // "SOCK_STREAM" means it will be a reliable connection (i.e., TCP;   // for UDP use SOCK_DGRAM), and I'm not sure what the 0 for the last   // parameter means, but it seems to work.   socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);   if (socketDescriptor < 0) {     cerr << "cannot create socket\n";     exit(1);   }     // Connect to server.  First we have to set some fields in the   // serverAddress structure.  The system will assign me an arbitrary   // local port that is not in use.   serverAddress.sin_family = hostInfo->h_addrtype;   memcpy((char *) &serverAddress.sin_addr.s_addr,          hostInfo->h_addr_list[0], hostInfo->h_length);   serverAddress.sin_port = htons(serverPort);                                                                                                                                       if (connect(socketDescriptor,               (struct sockaddr *) &serverAddress,               sizeof(serverAddress)) < 0) {     cerr << "cannot connect\n";     exit(1);   }     cout << "\nEnter some lines, and the server will modify them and\n";   cout << "send them back.  When you are done, enter a line with\n";   cout << "just a dot, and nothing else.\n";   cout << "If a line is more than " << MAX_LINE << " characters, then\n";   cout << "only the first " << MAX_LINE << " characters will be used.\n\n";     // Prompt the user for input, then read in the input, up to MAX_LINE   // charactars, and then dispose of the rest of the line, including   // the newline character.   cout << "Input: ";   cin.get(buf, MAX_LINE, '\n');   while (cin.get(c) && c != '\n')     ;     // Stop when the user inputs a line with just a dot.   while (strcmp(buf, ".")) {     // Send the line to the server.     if (send(socketDescriptor, buf, strlen(buf) + 1, 0) < 0) {       cerr << "cannot send data ";       close(socketDescriptor);       exit(1);     }       // Zero out the buffer.     memset(buf, 0x0, LINE_ARRAY_SIZE);       // Read the modified line back from the server.     if (recv(socketDescriptor, buf, MAX_LINE, 0) < 0) {       cerr << "didn't get response from server?";       close(socketDescriptor);       exit(1);     }       cout << "Modified: " << buf << "\n";       // Prompt the user for input, then read in the input, up to MAX_LINE     // charactars, and then dispose of the rest of the line, including     // the newline character.  As above.     cout << "Input: ";     cin.get(buf, MAX_LINE, '\n');     while (cin.get(c) && c != '\n')       ;   }     close(socketDescriptor);   return 0; } 3. Named Pipes Pipes can only be used by related processes. To allow unrelated processes to communicate the named pipe was introduced (it appears in a directory listing along with filenames and links).    A named pipe, or FIFO, is like an ordinary pipe in that it is a one-way first-in-first-out byte stream channel. They differ from normal pipes in that they are permanent (like a file), have a Unix file name, owner, size, access rights, can be opened, closed, deleted like a file, and can be used by unrelated processes.      For the most part programming with FIFO's is the same as with pipes. The main difference is in initialisation. To create a FIFO you can use the mknod command (at the shell prompt), e.g.      $mknod myfifo p   Here myfifo is the name of the pipe. You can also specify permissions if you want. When you do an ls, the fifo has a 'p' at the start of the line.   Alternatively, you can create the fifo in your program using the mknod system call, e.g.        #include           :      if (mknod("myfifo", 010755, 0) < 0)          perror("mknod failed"); or           fd = open("myfifo", O_WRONLY);   which would open a fifo for writing. It will block until another process opens it for reading. You can open the fifo with a non blocking call e.g.:        if ( fd = open("myfifo", O_WRONLY | O_NDELAY)) < 0)           perror("open failed on FIFO");   Future writes into the fifo will be non blocking.  The open call will return -1 is another process isn't trying to read from the fifo.  Apart from O_WRONLY you could have O_RDONLY or O_RDWR.   !!!!! make certain you create the pipe in the default directory for the process or give the full pathname.!!!!       // Sendmsg.cc - send messages via a fifo to another program // create fifo first, i.e. mknod fifo p // then run rcvmsg, i.e. rcvmsg & // the run this, i.e. sendmsg hello sam how are you #include #include #include #include #include   const int MSGSIZE = 64; extern int errno; char *fifo = "fifo";   int main(int argc, char *argv[]) {     int fd, j, nwrite;     char msgbuf[MSGSIZE+1];     if (argc < 2 )        {         cout << "Usage : sendmsg message..." << endl;         exit(1);        }     // open fifo with O_NDELAY set, probably easier to use     //  blocking writes unless you have reasons for non-blocking     if (( fd = open(fifo, O_WRONLY | O_NDELAY)) < 0)        {        cerr <<" FIFO open failed";        exit(-1);        }     // send message     for (j = 1; j < argc; j++)        {        if (strlen(argv[j]) > MSGSIZE)            {             cerr << "message too long " << argv[j] << endl;             continue;            }        strcpy(msgbuf, argv[j]);        if ((nwrite = write(fd, msgbuf, MSGSIZE+1)) <= 0)           {           if (nwrite == 0)    /* Full FIFO */               errno = EAGAIN;   /* Fake error */           cerr << "mesage write failed" << endl;           exit(-1);           }     }     return 0; } // rcvmsg.cc - receive message via FIFO pipe   #include #include #include #include   const int MSGSIZE = 64; char *fifo = "fifo";   int main(int argc, char *argv[]) {     int fd;     char msgbuf[MSGSIZE+1];     // Open FIFO for reading and writing     if ((fd = open(fifo, O_RDWR)) < 0 )        {        cerr << " FIFO open failure";        exit(-1);        }     // Receive message(s) forever     for (;;)        {        if (read(fd, msgbuf, MSGSIZE+1) < 0 )           {           cerr << " Message read failed";           exit(-1);           }        // print message        cout << "Message received: " << msgbuf << endl;        }     exit(0); }   4. Another way to communicate: Signals                                                                                                                                                         Figure 5     Signals provide a mechanism for asynchronous and synchronous event notification for processes. They may be generated with by the operating system or another process. The OS generates signals in response to both errors (two example are invalid memory reference or stack overflow) and process requested notifications such as timer expiration. Processes can request the OS to deliver a signal to another process by using the kill or sigsend functions. For synchronous notification a process may use the pause system call. Every signal has a name, specified in the header file . There are five conditions that generates signals: ·         The kill() system call allows a process to send a signal to another process or to itself. The name kill is somewhat a misnomer, since a signal does not always terminate a process. Some signals do indeed kill the receiving process, while others are used to inform the process of some condition that the receiving process then handles. This is the format of kill() system call: int kill(int pid, int sig); The kill() system call does many different things, depending on the values of its arguments. As expected, a process is not able to send a signal to any other arbitrary process. To send a signal, the sending process and the receiving process must belong to the same user, or the sending process must be the superuser. A parent can kill a child (or a child can kill itself) by using the kill() call:                                                                                                 #include                                                                                                 #include                    // defines: int kill(pid_t pid, int sig);                                                                                                 ...                                                                                                 kill(pid, 9);                                 // signal of 9 is unconditional kill ·         The kill command at the command prompt is also used to send signals. This command is a program that takes its command line arguments and issue a kill() system call. For example the kill –STOP send a SIGSTP signal  to the process with the process ID of . Certain terminal characters generate signals. For example, every interactive terminal has associated with it an interrupt character character. The interrupt character (typically Control-C or Delete) terminates a process that is running (it generate a SIGINT signal). A terminal suspend character (typically Control-Z) generates a SIGSTP signal immediately. Certain hardware conditions generate signals. For example referencing an address outside a process’ address space generates a SIGSEGV signal. Certain software conditions that are noticed by the kernel cause signals to be generated.   Having described what signals are and how they are generated, what can a process do with a signal? A process can provide a function that is called whenever a specific type of signal occurs. The function, called a signal handler, can do whatever the process wants to handle the condition. This is called catching the signal. A process can choose to ignore a signal. All signals other than SIGKILL, can be ignored. The SIGKILL signal is special, since the system administrator should have a guaranteed way of terminating any process.     A process can overwrite the default signal handler. for example (in C++)     Interprocess Signals - send a signal which can invoke a special handler #include #include #include void sighup(int); void sigint(int); void sigquit(int); using namespace std; int main(void)   {     int pid;   /* get child process */     if ((pid=fork()) < 0)     { perror("fork"); exit(1); }       if (pid == 0) {  /* child */           signal(SIGHUP, sighup);           signal(SIGINT, sigint);           signal(SIGQUIT, sigquit);           for(;;);   } else {  /* parent */           cout <<"PARENT: sending SIGHUP"< #include #include #include   #define SHMSZ     27   int main()   {     int shmid;     key_t key;     char c,  *s;                                void* shm;     key = 5678; /* selected key */       /* Create the segment.*/       if ((shmid = shmget(key,SHMSZ,IPC_CREAT | 0666)) < 0)     {         perror("shmget"); exit(1);     }       /* Now we attach the segment to our data space.*/     if ((shm = shmat(shmid, NULL, 0)) == (void*)-1) {         perror("shmat"); exit(1);     }       /* put some things into the memory */     for (s = (char*)shm, c = 'a'; c <= 'z'; c++) *s++ = c;     *s = '\0';       /* wait until first character is changed to '*' */                                                 s = (char*)shm;     while (*s != '*') sleep(1);     exit(0); }   /* shmread */ #include #include #include #include #define SHMSZ     27   int main()   {      int shmid;     key_t key;     char *s;     void* shm;       key = 5678; /* selected key by server */     /* Locate the segment. */       if ((shmid = shmget(key,SHMSZ,0666)) < 0)     {     perror("shmget"); exit(1);     }       /* Now we attach the segment to our data space. */       if ((shm = shmat(shmid, NULL, 0)) ==  (void*) -1)    {         perror("shmat"); exit(1);     }       /* read what the server put in the memory. */      for (s = (char *)shm; *s != '\0'; s++)                                         std::cout << *s << std::endl;                                                                                                             std::cout << std::endl;       /* change the first character in segment to '*' */                                                                                                 s=(char*)shm;    *s= '*';    exit(0);   }