Principle Time: 2002-7-16 9:31:52 The I / O command set of UNIX systems is evolved from the commands in the Maltics and early systems, and their mode is open-write (Open-Write- Read-close. When a user process performs I / O operation, it first calls "Open" to get the right to specify the specified file or device, and return to the integer of the file descriptor to describe the user on the open file or device. The process of I / O operation. The user process then calls "read / write" multiple times to transfer data. When all transfer operations are completed, the user process turns off call, and notify the operating system has completed the use of an object. When the TCP / IP protocol is integrated into the UNIX kernel, a new type of I / O operation is equivalent to the UNIX system. The interaction of UNIX user processes and network protocols is much more complicated than user processes and traditional I / O devices. First, two processes for network operations are on different machines, how to establish their connections? Secondly, there are a variety of network protocols, how to build a general mechanism to support multiple protocols? These are issues to be solved by the network application programming interface. In UNIX systems, there are two types of network application programming interfaces: sockets (socket) and UNIX System V. TLI. Since Sun has adopted UNIX BSD operating systems that support TCP / IP, the application of TCP / IP has a greater development, and its network application programming interface - sockets are widely used in network software, so far Introducing the microcomputer operating system DOS and Windows systems, becoming a powerful tool for developing network applications, this chapter will discuss this issue in detail. 2.2 Setting The basic concepts of the text programming must first establish the following concepts before starting using a socket programming. 2.2.1 Network Processes Communication Process Communication The concept of communication is originally derived from a stand-alone system. Since each process runs within its address range, the operating system provides corresponding facilities for process communication between the two mutually communicative processes, the operating system provides the process communication, such as the pipeline in UNIX BSD (PIPE Named PIPE and Soft Interrupt Sign (SIGNAL), Message, Shared Memory, and Semaphore, etc., but are limited to use in native processes Inter-communication. Network process communication should be solved by mutual communication issues between different host processes (which can be used as a special case for the same machine process). To this end, the first thing to solve is the problem of network process identification. On the same host, different processes can be uniquely identified. However, in a network environment, the process numbers independently allocated by each host cannot uniquely identify the process. For example, host A is paired with a process number 5, and there is also a process in the B machine, so the "No. 5 process" is meaningless. Second, the network protocol supported by the operating system has different ways of working in different protocols, and the address format is different. Therefore, network process communication also solves the identification problem of multiple protocols. In order to solve the above problems, the TCP / IP protocol introduced the following concepts. A communication port that can be named and addressed in the port network is a resource that the operating system is assignable. According to the description of the OSI seven-layer protocol, the maximum difference between the transport layer and the network layer in function is to provide process communication capabilities. In this sense, the final address of network communication is more than just host addresses, but also an identifier that can describe the process.
To this end, the TCP / IP protocol proposes the concept of protocol port (PROTOCOL Port, referms) to identify the process of communication. The port is an abstract software structure (including some data structures and I / O buffers). After the application (ie process) is connected to a bit, the data transmitted to the port is received by the system call, and the data transmitted to the port is received by the corresponding process, and the corresponding process is transmitted to the transport layer through the port output. In the implementation of the TCP / IP protocol, port operations are similar to a general I / O operation, and the process gets a port, which is equivalent to obtaining a local unique I / O file, which can be accessed with a general read and write primitive. Similar to the file descriptor, each port has an integer identifier of port number, which is used to distinguish different ports. Since two protocols of the TCP / IP transport layer TCP and UDP are completely independent software modules, their respective port numbers are also independent of each other, such as TCP has a 255-port port. UDP can also have a 255-port, both Not conflict. The allocation of the port number is an important issue. There are two basic allocations: the first kind of global allocation, this is a centralized control method, and a recognized central organization is unified according to the user needs, and will be published in the results. The second is local allocation, also known as dynamic connection, that is, when the process needs to access the transport layer service, apply to the local operating system, and the operating system returns a local unique port number, and the process will use the port by the appropriate system call. The number is linked (tied). The above two methods are integrated in the allocation of the TCP / IP port number. TCP / IP divides the port number into two parts, a small amount of retention port, assigned to the service process in a global manner. Therefore, each standard server has a globally recognized port (ie, Well-KNown Port), even in different machines, its port number is also the same. The remaining port is allocated in a local manner. TCP and UDP are all specified that the port number of less than 256 can be reserved. The two processes of communication in address network communications are in different machines. In an interconnect network, two machines may be located in different networks, which are connected through a network interconnect device (gateway, bridge, router, etc.). Therefore, a three-level addressing: 1. A host can be connected to multiple networks, you must specify a specific network address; 2. Each host on the network should have its unique address; 3. Each host The process should have a unique identifier on that host. Typically, host addresses are composed of network ID and host ID, and use a 32-bit integer value in the TCP / IP protocol; TCP and UDP use a 16-bit port number to identify user processes. The network byte sequence is different from the order of the multi-byte value, and some machines store the low byte (low price first), some save high-bit bytes (high-priced first). In order to ensure the correctness of the data, the network byte order must be specified in the network protocol. TCP / IP protocol uses 16-bit integers and 32-bit integers, which are included in the protocol header file. Communication link between two processes is called a connection. Connected to internal performance is some buffer and a set of protocol mechanisms, externally externally reserved the reliability of no connection. Semi-related, in the network, a three-way group can be used in the global unique mark: (protocol, local address, local port number) such a three-way group called a semi-associated (Half-Association), specified Connect every half of the connection.
All relevant a complete network process communication needs to be composed of two processes and can only use the same high-level protocol. That is to say, it is impossible to communicate with the TCP protocol, and the other ends with the UDP protocol. So a complete network communication requires a five-yuan group to identify: (protocol, local address, local port number, remote address, remote port number) such a five-way group, called a association, ie two The same semi-correlation in the protocol can be combined into a suitable correlation, or fully specify a connection. 2.2.2 Service Method In the network hierarchical structure, each layer is strictly oriented, and the division of labor and collaboration are reflected in the interface between adjacent layers. "Services" is an abstract concept that describes the relationship between adjacent layers, that is, each layer in the network is in a set of operations provided close to the upper layer. The lower layer is the service provider, the upper layer is the user of the request service. The performance of the service is primitive, such as system calls or library functions. The system call is the service primitive that the operating system is available to the network application or high-level protocol. The N layer in the network will always provide a more complete service than N-1 layers to the N 1 layer, otherwise the N-layer does not exist. In OSI's terms, the network layer and the following layers thereof are also referred to as communication subnets, only point-to-point communication, no programs or processes. The transport layer is implemented, and the network process communication concept is introduced, and it is also necessary to solve the error control, traffic control, data sort (packet sorting), connection management, etc., provide different service methods for this : Connected (virtual circuit) or no connection-oriented connection service is an abstraction of the telephone system service mode, that is, each complete data transfer is established, using a connection, and terminating the connection. During the data transfer, each data packet does not carry the destination address, and the connection number (Connect ID) is used. Essentially, the connection is a pipeline, and the transmission and reception data is not only the same, but also the same content. The TCP protocol provides a connected virtual circuit. The connectionless service is an abstraction of the postal system service, and each packet carries a complete destination address, each packet is transmitted independently in the system. No connection service does not guarantee the order of the group, and the recovery and retransmission of grouping errors are not performed, and the reliability of the transmission is not guaranteed. The UDP protocol provides a connectionless datagram service. The type and application of these two services is given below: the service type service example is a non-reliable connection file transmission (FTP) remote login (FTP) digital voice without connection. Reliable Data Reporting Datasheet Request - Answer E-mail (E-mail) Regular Swage Network Database Query Order In Network Transmission, two consecutive packets may pass different paths in terminal-end communication, This may be different from the transmission when reaching the destination. "Order" means that the sequence of reception data is the same as the sequence of transmitted data. The TCP protocol provides this service. Error Control guarantees a mechanism for the data received by the application. Checking the error method is generally a method of testing "Checksum). It is guaranteed that there is no error-free approach to the two parties use a confirmation response technology. The TCP protocol provides this service. Flow control controls a mechanism for controlling the rate of data transmission during data transmission to ensure that the data is not lost. The TCP protocol provides this service. The byte stream byte flow means that only the packets in the transfer are regarded as a byte sequence, and no boundaries of the data stream are provided. The TCP protocol provides a word throttle service. The message receiver is to save the sender's packet boundary. The UDP protocol provides packet services. The full-duplex / half-duplex-end data is transmitted in two directions / one direction.
Cache / with external data In byte stream, since there is no message boundary, the user process can read or write any number of bytes at a certain time. To ensure that the transfer is properly or uses a streaming protocol, it is to be cached. But for some special needs, such as interactive applications, will request cancel this cache. During data transfer, it is desirable to transfer some type of information to the user in time by conventional transmission, such as the interrupt key of the UNIX system (delete or control-c), terminal stream control (Control-S and Control-Q). ), Called out-of-band data. Logically, as if the user process uses a separate channel to transfer this data. This channel is associated with the flow of each pair of connections. Since the implementation of the extracted data in Berkeley Software Distribution is inconsistent with the Host Agreement specified in RFC 1122, the application writer is required to subtract the problem in interoperability, unless the external data is required, unless otherwise provided with existing services. It is best not to use it. 2.2.3 Customer / Server Mode In TCP / IP Network Applications, the main mode of the two processes between communication is the client / server mode, that is, the customer issues a service request to the server, the server receives the request. Once, provide the corresponding services. The establishment of the client / server mode is based on the following two points: First, the cause of the network is the soft hardware resource, calculation capacity, and information in the network. It needs to be shared, thus making the host providing services with many resources, and customers request services. This is a non-equal role. Second, the network process communication is completely asynchronous. There is neither a parent-child relationship between the processes of communication between each other, and does not share the memory buffer, so there is a mechanism to establish a connection between the processes that wish to communicate, provide data exchange for both Synchronization, this is based on TCP / IP of customer / server mode. Customer / Server Mode takes place during operation: First, the server side must start first, and provide the appropriate service according to the request: 1. Open a communication channel and inform the local host, it is willing to be on a recognized address (Zhou Zicang) Port, such as FTP is 21) Receive customer requests; 2. Waiting for the client request to reach this port; 3. Receive the repeated service request, process the request and send a response signal. Receive concurrent service requests, to activate a new process to handle this customer request (such as in the UNIX system with fork, exec). The new process handles this customer request and does not need to respond to other requests. After the service is complete, turn off the new process with the customer's communication link and terminate. 4. Return to the second step and wait for another customer request. 5. Turn off the server client: 1. Open a communication channel and connect to the specific port of the host where the server is located; 2. Send a service to the server, wait and receive a response; continue to ask a request ... 3. The communication channel is turned off after the request is completed. From the above description, you can see: 1. The role of the client and the server process is unsmark, so the coding is different. 2. The service process is generally started before the customer request. As long as the system is running, the service process has always exist until it is normal or forced to terminate. 2.2.4 Socket Type TCP / IP Socket provides the following three types of sockets. Flow socket (SOCK_STREAM) provides a connection, reliable data transfer service, no error in data, no repeatedly transmitted, and is received in sequence. Internal flow control, avoiding data flow overrun; data is considered to be byte stream, no length limit. File Transfer Protocol (FTP) is based with stream socket.
Data reporting (SOCK_DGRAM) provides a connectionless service. The packet is sent in the form of an independent packet, does not provide an error, the data may be lost or repeated, and the reception order is confusing. Network File System (NFS) uses a datagram socket. Original socket (SOCK_RAW) This interface allows direct access to a lower layer protocol, such as IP, ICMP. Commonly used to check new protocols implementation or access new devices configured in existing services. 2.3 Basic socket system call To better illustrate the socket programming principle, the following is given below for several basic socket system call descriptions. 2.3.1 Creating a socket - Socket () Application First Before using the socket, you must first have a socket, the system calls socket () provides the means to create a socket for the application, and its call format is as follows: Socket Pascal Far Socket (Int Af, INT TYPE, INT Protocol); This call is to receive three parameters: AF, TYPE, Protocol. The parameter AF specifies the area of communication, the address family supported by UNIX system has: AF_UNIX, AF_INET, AF_NS, etc., while only AF_INET is supported in Windows, which is an Internet area. Therefore, the address family is the same as the protocol. The parameter Type describes the type of socket to be established. Parameter protocol Describes the specific protocol used by the socket, if the caller does not want to specify the protocol to be used, set to 0, use the default connection mode. Establish a socket based on these three parameters and assign the corresponding resource to it, and return a integer socket. Therefore, the Socket () system call actually specifies the "protocol" in the relevant five-component group. See 5.2.23 for the detailed description of Socket (). 2.3.2 Specify the local address - Bind () When a socket is created with socket (), there is a name space (address family), but it is not named. Bind () links the socket address (including the local host address and local port address) with the created socket, which will give a socket to specify local semi-associated. The invocation format is as follows: int Pascal Far Bind (socket S, const strunt sockaddr far * name, int namelen); Parameter S is a socket descriptor (socket) that is returned by socket () calls and not connected . The parameter NAME is a local address (name) assigned to the socket, which is variable, and the structure is different from the communication domain. Namelen demonstrates the length of the Name. If there is no error, bind () returns 0. Otherwise the return value socket_ERROR is returned. The address plays an important role in establishing socket communication, as a network application designer must have a clear understanding of the socket address structure. For example, UNIX BSD has a set of data structures describing the socket address, where the address structure of the TCP / IP protocol is: struct sockaddr_in {short sin_family; / * AF_INET * / U_SHORT SIN_PORT; / * 16-bit port number, network byte Sequence * / struct in_addr sin_addr; / * 32-bit IP address, network byte order * / char sin_zero [8]; / * Reserved * /} For more detailed descriptions of bind () See 5.2.2. 2.3.3 Creating a socket connection - inconnect () with accept () These two system calls are used to complete a completely related establishment, where connect () is used to establish a connection. The connectionless socket process can also call connect (), but there is no actual message exchange between the processes, and the call will return directly from the local operating system.
The advantage of this is that the programmer does not have to specify the destination address for each data, and if a data report received, the destination port does not establish "connection" with any socket, it is possible to determine if the port is unacceptable. ACCEPT () is used to allow the server to wait for the actual connection from a client process. The CONNECT () call format is as follows: Int Pascal Far Connect (socket S, const strunt sockaddr far * name, int namelen); Parameter S is to establish a local socket descriptor for the connection. The parameter Name indicates a pointer to the other side socket address structure. The other side socket address length is described by Namelen. If there is no error, Connect () returns 0. Otherwise the return value socket_ERROR is returned. In the connection-oriented protocol, the call leads to the actual establishment between the local system and the external system. Since the address family is always included in the first two bytes of the socket address structure, and via the socket () call is related to a certain protocol family. Therefore, bind () and connect () do not have to agree to be used as parameters. See 5.2.4 for detailed descriptions of Connect (). The call format of Accept () is as follows: Socket Pascal Far Accept (socket S, Struct SockAddr Far * AddR, int far * addrlen; parameter S is the local socket descriptor, before the parameters called an Accept () call Call Listen (). Addr points to the pointer of the client-square socket address structure to receive the address of the connection entity. The exact format of Addr is determined by the address family established when the socket is created. Addrlen is the length of the client's plug-in address (number of bytes). If there is no error, accept () returns a value of a socket type to indicate the descriptor of the received socket. Otherwise, return value invalid_socket. Accept () is used to facilitate the connection server. Parameters AddR and Addrlen store the client's address information. Before the call, the parameter addr points to an address structure of an initial value, and the initial value of AddRlen is 0; after calling accept (), the server waits to accept the client connection request from the socket number S, and the connection request is The client's connection is sent. When there is a connection request arrives, the Accept () calls the first client plug-in address and length on the request connection queue into the ADDR and AddRlen, and create a new socket number with the same characteristics as S. New sockets can be used to handle the server concurrent request. See 5.2.1 for a detailed description of Accept (). Four socket system calls, socket (), bind (), connect (), acception (), can complete a complete five-yuan related establishment. Socket () Specifies the protocol element in the five-component group, its usage is not related to whether or not the client or server is facing. Bind () Specifies the local binary, namely the local host address and port number in the five-component group. Its usage is related to whether or not the connection is connected: in the server side, whether it is connected, both bind (); in the customer, if With the connection, bind () can be not adjusted, and automatic is automatically completed by connect (). If you use no connection, the client must use bind () to get a unique address. The above discussion is only in terms of customer / server mode, and the use of the socket is very flexible. The only principle that needs to follow is before the process communication, it must establish a complete correlation. 2.3.4 Listening Connections --Listen () This call is used to facilitate the connection server indicating that it is willing to receive connections. Listen () needs to be called before accept (), which calls the format as follows: int Pascal Far Listen (Socket S, INT Backlog); Parameter s Identify a local established, unconnected socket, the server is willing to receive it from it request.
BACKLOG represents the maximum length of the request connection queue, which is used to limit the number of queuing requests, and the maximum allowed is 5. If there is no error, Listen () returns 0. Otherwise it returns socket_ERROR. Listen () can do the necessary connections to complete the socket S without calling BIND () during the execution call, and establishes the length of the request connection queue for BACKLOG. Calling listen () is the third step in the four steps of the server to receive a connection request. It calls socket () allocates a stream sleeve and calls BIND () to call the S after being called, and must be called before accept (). See 5.2.13 for a detailed description of Listen (). Section 2.2.3 Refer to two types of services: duplicate services and concurrency services in Customer / Server Mode: Repeated service and concurrent services. Accept () call provides great convenience to implement concurrent services, because it is necessary to return a new socket, which is typically: int initsockid, newsid; if (((INITSOCKID = Socket (....)) < 0) Error ("can't create socket"); if (Bind (INITSOCKID, ....) <0) Error ("Bind Error"); IF (Listen (INITSOCKID, 5) <0) Error ("Listen "); for (;;) {newsockid = accept (intesockid, ...) / * block * / if (newsockid <0) error (" accept error "); if (fork () == 0) {/ * Sub-process * / closssocket (INITSOCKID); do (newsockid); / * Process request * / exit (0);} closesocket (newsockid); / * Parent process * /} The result of this program is NewsockID and customers Setting related, after the child process is started, turn off the initsockid of the primary server inherited, and use the new NewsockID to communicate with the customer. The main server's INITSOCKID will continue to wait for a new client connection request. Since the multi-task system in Unix et al., In system scheduling, multiple processes can be performed simultaneously. Therefore, using the concurrent server allows the server process to have multiple child processes and different client programs at the same time. In the customer program, the server can handle the request of multiple customers at the same time, which is the origin of the concurrent server name. The connection server can also be a repetitive server, which is as follows: int initsockid, newsckid; if ((IF (("can't create socket); if (" can't create socket); if (Bind (INITSOCKID) , ....) <0) Error ("Bind Error"); IF (Listen (INITSOCKID, 5) <0) Error ("Listen Error"); for (;;) {newsockid = accept (INITSOCKID, .. .) / * Block * / if (NewsockID <0) Error ("Accept Error"); Do (NewsockID); / * Processing Request * / CloseSocket (NEWSOCKID);} Repeat server can only establish with a customer program at a time Connect, it is repeatedly done by the processing of multiple client programs, and is called repetitive servers.
Concurrent servers and repeated servers have adverse and disadvantages: the concurrent server can improve the response speed of the client program, but it increases the overhead of system scheduling; the repetition server is just in contrary to it, so when deciding whether to use concurrent servers or repeat the server, The actual situation is time. 2.3.5 Data Transmission - Extend () and RECV () After a connection is established, you can transfer data. Common system calls have Send () and RECV (). Send () calls to send output data on the connected datagram or flow sleeve specified in parameter s, the format is as follows: Int Pascal Far Send (Socket S, Const Char Far * Buf, Int Len, Int Flags); The parameter S is a connected local socket descriptor. BUF points to a pointer to the buffer of transmitting data, and its length is specified by LEN. Flags Specifies the transmission control mode, such as whether or not to send out external data. If there is no error, Send () returns the number of bytes sent in total. Otherwise it returns socket_ERROR. See 5.2.19 for the detailed description of Send (). RECV () calls to receive input data on the connected datagram or flow sleeve design specified in parameter s, the format is as follows: Int Pascal Far Recv (Socket S, CHAR FAR * BUF, INT LEN, INT FLAGS); parameters s is a connected socket descriptor. BUF points to the pointer to the input data buffer, and its length is specified by LEN. Flags Specifies the transmission control mode, such as receiving out-of-band data. If there is no error, RECV () returns a total of the total number of bytes. Returns 0 if the connection is turned off. Otherwise it returns socket_ERROR. See 5.2.16 for a detailed description of RECV (). 2.3.6 Input / Output Multiplexing - Exist () select () calls to detect the status of one or more sockets. For each socket, this call can request information about reading, writing, or error status. The socket collection requesting a given state is indicated by an FD_set structure. When returning, this structure is updated to reflect those subsets that meet the sockets of a particular condition, and select () calls to return the number of sockets that meet the condition, and its call format is as follows: int PASCAL FAR SELECT (INT) NFDS, FD_SET FAR * READFDS, FD_SET FAR * WRITEFDS, FD_SET FAR * EXCEPTFDS, CONST STRUCT TIMEVAL FAR * TIMEOUT; the parameter NFDS indicates the value domain of the check-in descriptor, which is generally ignored. The parameter readfds point to the pointer to which the socket descriptor set to be read, the caller wants to read the data from the present. The parameter WriteFDS points to the pointer to the socket descriptor collection to be written. ExceptFDS points to a pointer to the set of socket descriptors to be detected. Timeout points to the maximum time waiting for the select () function, if set to null, is a blocking operation. SELECT () Returns the total number of socket descriptors that have been prepared in the fd_set structure, or return socket_ERROR. See 5.2.18 for Detailed Description of SELECT (). 2.3.7 Closure Sockets --closeSocket () CloseSocket () Close the socket S and releases the resource assigned to the socket; if S involves an open TCP connection, the connection is released. The call format of CloseSocket () is as follows: BOOL PASCAL FAR CloseSocket (socket S); the parameter S is ready to close the socket descriptor. If there is no error, CloseSocket () returns 0. Otherwise the return value socket_ERROR is returned. See 5.2.3 for a detailed description of CloseSocket ().
2.4 Typical Socket Calling Processes For example, the application of TCP / IP protocols generally adopt client / server mode, so in practical applications, there must be two processes of customers and servers, and start the server first, whose system calls the timing The figure is as follows. Socket system call for connection-oriented protocols (such as TCP) is shown in Figure 2.1: The server must start first until it performs accept () call, after entering the waiting state, can receive the customer request. If the customer is started before, connect () will return an error code and the connection is unsuccessful. Figure 2.1 Connecting socket system call timing diagram There is no connection protocol to the symptom call is shown in Figure 2.2 shows: Figure 2. Sockets of the connection protocol Call timing diagram No connection server must also start, otherwise the customer request Can't ask for a service process. No connection customers do not call connect (). Therefore, before the data is sent, the customer and the server has not yet been established, but it has been established by socket () and bind (). When sending data, the sender needs to specify the receiver socket number by specifying the local socket number, except for the local socket, and is dynamically established in the data transceiver. Example This instance uses a client / server mode facing the connection protocol, which is shown in Figure 2.3: Figure 2.3 Connected application flowchart Server program: / * file name: streams.c * / #include #include #define True 1 / * This program creates a socket and then starts an unlimited loop; each time it receives a connection by loop, print an information. When the connection is disconnected, or the termination information is received, this connection ends, and the program will receive a new connection.
The format of the command line is: streams * / main () {int Sock, Length; struct sockaddr_in server; struct sockaddr tcpaddr; int msgsock; char buf [1024]; int RVAL, LEN; / * Create a socket * / sock = Socket (AF_INET, SOCK_STREAM, 0); if (SOCK <0) {Perror ("Opening Stream Socket"); EXIT (1);} / * Using any port naming socket * / server.sin_family = AF_INET; Server. SIN_PORT = INADDR_Any; if (Sock (STRUCK, STRUCKADDR *) & Server, SIZEOF (Server)) <0) {Perror ("Binding Stream Socket"); EXIT (1);} / * Find the specified port number and Print out * / length = sizeof (server); if (GetSockName (STRUCKAME (STRUCKADDR *) & Server, & length) <0) {Perror ("getting socket name"); exit (1);} printf ("Socket Port #% d / n ", ntohs (server.sin_port)); / * Start receiving connection * / listen (Sock, 5); len = sizeof (struct sockaddr); do {msgsock = accept (Sock, (Struct SockAddr *) & tcpaddr, (int *) & len; if (msgsock == -1) PERROR ("accept"); Else Do {Memset (BUF, 0, SIZEOF (BUF)); IF ((RVAL = RECV (Msgsock, BUF, 1024)) <0) PERROR ("Reading Stream Message"); if (rval == 0) Printf ("ending connection / n"); Else Printf ("->% s / n" , BUF);} while (rval! = 0); CloseSocket (Msgsock);} while (true); / * Because this program already has an infinite loop, the socket "SOCK" never explicitly shut down. However, when the process is killed or normally, all sockets will be automatically closed. * / EXIT (0);} Customer program: / * file name: streamc.c * / #include #include #define data "Half a League, Half a League ..." / * This program establishes a socket, Then connected to the socket with the command line; the connection is ended, and the connection is sent, and then the socket is turned off.
The format of the command line is: Streamc hostname port number port number is to be the same * / main (argc, argv) int Argc; char * argv []; {Int Sock; struct sockaddr_in server; struct hostent * hp , * gethostByname (); Char BUF [1024]; / * Creating a socket * / sock = socket (AF_INET, SOCK_STREAM, 0); if (SOCK <0) {Perror ("Opening Stream Socket"; EXIT (1 } / * Use the name connection socket specified in the command line * / server.sin_Family = AF_INET; HP = gethostByname (Argv [1]); if (hp == 0) {fprintf (stderr, "% s: Unknown host / n ", argv [1]); Exit (2);} Memcpy ((char *) & server.sin_addr, (char *) hp-> h_addr, hp-> h_length; sever.sin_port = htons (atoi (Argv [2])); if (Connect (STRUCK, STRUCKADDR *) & Server, SIZEOF (Server)) <0) {Perror ("Connecting Stream Socket"); EXIT (3);} IF (SOCK , DATA, SIZEOF (DATA)) <0) PERROR ("Sending On Stream Socket"); CloseSocket (SOCK); EXIT (0);} 2.5 A universal instance program In the previous section, we introduced a simple Socket program instance. From this example we can see that there is almost a mode using the socket program, that is, all programs have almost no exception to call the same order in the same order. So we can envisage, design a middle layer, provide a few simple functions upwards, and the program can achieve data transfer in normal cases as long as you call these functions, and the program designer does not have to care too much about the details of the Socket program design. In this section, we will introduce a generic network program interface that provides a few simple functions to the upper layer, and program designers can complete the vast majority of network data transfer as long as they use these functions. These functions are separated from the socket program and the upper layer, which uses the connection-oriented stream socket, using a non-blocking working mechanism, and the program will call these functions to query the network message and make a corresponding response. These functions include: l INITSOCKETSSTRUCT: Initialize the Socket structure, get the server port number. The client is used. l initPassiveSock: Initialize the Socket structure, obtain the server port number, and establish the main cover. Server program is used. l ClosemainSock: Close the main cover. Server program is used. l CreateConnection: Establish a connection. The client is used. l AcceptConnection: Receive connection. Server program is used. l CloseConnection: Close the connection. l QuerySocketsmsg: Query socket message. l Sendpacket: Send data. l Recvpacket: Receive data.
2.5.1 Header file / * file name: tcpsock.h * / / * header file includes a system header file that is often used by the socket program (which is given in this example is the header file under SCO UNIX, the other version of UNIX header The file may be slightly different and define our own two data structures and its real-alone variables, as well as the function described. * / #Include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct SocketsMsg {/ * message socket structure * / int AcceptNum; / * Indicates whether there is a foreign connection waiting to receive * / int ostnum; / * There is a number of connections waiting for reading * / int readyueue [32]; / * There are external data waiting to read the connection queue * / int WRITENUM; / * Send data * / int WRITEQUE [32]; / * can send data connection queue * / int exceptnum; / * has exceptions * / int exceptQueue [32]; / * has exceptional connection queue * / } Socketsmsg; typedef struct sockets {/ * socket structure * / int daemonSock; / * Main cover text * / int sucknum; / * Data socket number * / int sockets [64]; / * Data socket Array * / fd_set readfds, writefds, exceptfds; / * to be readable, writable, exceptional socket collection * / int port; / * port number * /} sockets; sockets mysock; / * global variable * / SocketsMsg SockMsg; int InitSocketsStruct (char * servicename); int InitPassiveSock (char * servicename); void CloseMainSock (); int CreateConnection (struct in_addr * sin_addr); int acceptConnection (struct in_addr * IPaddr); int CloseConnection (int Sockno); Int QuerySocketsmsg (); int sendpacket (int Sockno, Void * BUF, INT LE N); int cableNo, Void * BUF, INT Size; 2.5.2 Function Source File / * File Name: Tcpsock.c * / / * This file gives the source code of nine functions, some of which give Chinese Note * / #include "tcpsock.h" int initsocketsstruct (char * service "/ * Initialize sockets structure. If succeed the return 1, Else Return Error Code (<0) * / / * This function is only required The client of the socket is used to obtain service information.
Service definitions in / etc / services file * / {struct server * servrec; struct sockaddr_in serv_addr; if ((servrec = getservbyname) == null) {RETURN (-1);} Bzero (char *) & Mysock, sizeof (Sockets)); Mysock.Port = servrec-> s_port; / * Service Port in Network Byte Order * / return (1);} int InitPassiveSock (char * servicename) / * Initialize Passive Socket. If succeed the return 1, Else Return Error Code (<0) * / / * This function is used to take a server program that needs the passive sleeve. In addition to obtaining the service information, a passive sleeve is created. * / {INT MAINSOCK, FLAG = 1; struct server * servrec; struct sockaddr_in serv_addr; if ((servrec = getservbyname)) == null) {RETURN (-1);} Bzero ((char *) ) & Mysock, sizeof (sockets)); mysock.port = servrec-> s_port; / * service port in network byte order * / if ((mainsock = socket (AF_INET, SOCK_STREAM, 0) <0) {Return (-2 );} bzero ((char *) & serv_addr, sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl (INADDR_ANY); / * any network interface * / serv_addr.sin_port = servrec-> s_port; IF (Bind (mainsock, (struct socaddr *) & serv_addr, sizeof (serv_addr)) <0) {Close (MAINSOCK);} IF (Listen (MAINSOCK, 5) == -1) {/ * The active socket is changed to a passive sleeve, ready to receive the connection * / close (mainsock); return (-4);} / * set this socket as a non-blocking socket. * / If (IOCTL (MAINSOCK, Fionbio, & flag) == -1) {Close (mainsock); Return (-5);} mysock.daemonSock = mainsock; fd_set (mAinsock, & mysock.readfds); / * Declaration to the main cover "Read" Interest * / fd_set (MAINSOCK, & MYSOCK.EXCEPTFDS); / * Decliminated the exception of the main cover * / return (1);} void ClosemainSock () / * Close the main sleeve and clear the declaration of the above event.
Close the socket at the end of the main program is a good habit * / {close (Mysock.DaemonSock); FD_CLR (Mysock.DaemonSock, & Mysock.readfds); FD_CLR (Mysock.DaemonSock, & Mysock.exceptfds);} int CreateConnection (struct in_addr * sin_addr) / * Create a Connection to remote host which IP address is in sin_addr Param:.. sin_addr indicates the IP address in Network Byte Order if succeed return the socket number which indicates this connection, else return error code (<0) * / {struct sockaddr_in server; / * server address * / int TmpSock, Flag = 1, i; if ((tmpsock = socket (AF_INET, SOCK_STREAM, 0) <0) Return (-1); server.sin_family = AF_Inet Server.sin_port = mysock.port; server.sin_addr.s_addr = sin_addr-> s_addr; / * set this socket as a non-blocking socket. * / if (ioctl (tmpsock, fionbio, & flag) == -1) { Close (Tmpsock); RETURN (-2);} / * connect to the server. * / if (Connect (Tmpsock, Struct SockAddr *) & Server, Sizeof (Server)) <0) {IF ((Errno! = EWouldblock) ) && (errno! = einprogress)) {/ * If the error code is EWOULDBLOCK and EinProgress, you don't have to turn off the socket because the system will continue for a set. The connection is established, and whether the connection establishes successfully available SELECT () function to detect if the socket is "writable" to determine.
* / close (tmpsock); RETURN (-3); / * connect error. * /}} fd_set (tmpsock, & mysock.readfds); fd_set (tmpsock, & mysock.writefds); fd_set (tmpsock, & mysock.exceptfds); i = 0; while (mysock.sockets [i]! = 0) i ; / * Look for a blank sockets position * / if (i> = 64) {close (tmpsock); Return (-4); / * Too Many connections * /} Mysock.Sockets [i] = tmpsock; Mysock.SockNum ; return (i);.} int acceptConnection (struct in_addr * IPaddr) / * Accept a connection If succeed, return the data sockets number, else return -1 * / {Int Newsock, Len, Flag = 1, I; Struct SockAddr_in Addr; Len = SizeOf (AddR); Bzero ((Char *) & addr, len); if ((NEWSOCK = Accept (MySock.daemonsock, & Addr, & len) == -1) Return (-1); / * Accept error. * / / * set this socket as a non-blocking socket. * / ioctl (newsock, fionbio, & flag); fd_set (newsock, & mysock. readfds); FD_SET (newsock, & Mysock.writefds); FD_SET (newsock, & Mysock.exceptfds);. / * Return IP address in the Parameter * / IPaddr-> s_addr = addr.sin_addr.s_addr; i = 0; while (Mysock . Sockets [ I]! = 0) i ; / * Look for a blank sockets position * / if (i> = 64) {close (news); return (-4); / * Too Many Connections * /} mysock.sockets [i ] = news;}; return (i);} int closeconnection (int suckno) / * close a connection indecode; if ((sockno> = 64) || (sockno <0) || (mysock.sockets [sockno] == 0)) Return (0); retcode = close (mysock.sockets [sockno]); fd_clr (mysock.sockets [sockno], & mysock.readfds); fd_clr (MySock.sockets) [Sockno], & mysock.writefds; fd_clr (mysock.sockets [sockno], & mysock.exceptfds; mysock.sockets [sockno] =
0; Mysock.SockNum--; return (retcode);...} Int QuerySocketsMsg () / * Query Sockets Message If succeed return message number, else return -1 The message information stored in struct SockMsg * / {fd_set rfds, wfds , EFDS; int RETCODE, I; Struct TimeVal Timeout; RFDS = mysock.readfds; wfds = mysock.writefds; efds = mysock.exceptfds; timeout.tv_sec = 0; / * Return immediately, not blocked.
* / Timeout.tv_usec = 0; Bzero ((char *) & sockmsg, sizeof (sockmsg)); if ((Retcode = SELECT (64, & RFDS, & WFDS, & EFDS, & Timeout) == 0) Return (0); IF (Fd_isset (mysock.daemonsock, & rfds)) sockmsg.acceptnum = 1; / * some client call server. * / For (i = 0; i <64; i ) / * data in message * / {ix ((mysock). Sockets [i]> 0) && (fd_isset (mysock.sockets [i], & rfds))) sockmsg.readqueue [sockmsg.readnum ] = i;} for (i = 0; i <64; i ) / * data OUT Ready Message * / {IF ((mysock.sockets [i]> 0) && (fd_isset (mysock.sockets [i], & wfds))) sockmsg.writequeue [sockmsg.writenum ] = i;} if (fd_isset (MySock. Daemonsock, & EFDS)) sockmsg.acceptnum = -1; / * server socket error. * / For (i = 0; i <64; i ) / * error message * / {ix ((mysock.sockets [i]> 0 ) && (fd_isset)) SockMsg.ExceptQueue [SockMsg.Exceptnum ] = i;} return (Retcode);} int sendpacket (int Sockno, Void * buf, int LEN) / * Send a packet. if succeed returnid the number of send data, else return; = {((sockno> = 64) || (Sockno <0) || (mysock.sockets [sockno] == 0)) Return (0); IF ((Actlen = Send (mysock.sockets [sockno), buf, len, 0)) <0) Return (-1 ); return (actlen);.} int RecvPacket (int Sockno, void * buf, int size) / * Receive a packet If succeed return the number of receive data, else if the connection is shutdown by peer then return 0, otherwise return 0-errno * / {Int actlen; IF ((Sockno> = 64) || (sockno <0) || (mysock.sockets [sockno] == 0)) Return (0); if (((Actlen = Recv) Mysock.sockets [sockno], buf, size, 0)) <0) Return (0-errno);
Return (actlen); / * actlen is the received data length, if zero, indicating that the connection is turned off by the other party. * /} 2.5.3 Simple server program example / * file name: server.c * / / * This is a very simple repetition server program that initializes the passive sleeve, and the loop waits for the reception connection. If the connection is received, it displays the data socket serial number and the client's IP address; if there is data on the data socket, it receives the data and displays the data socket number and the received string. * / #include "tcpsock.h" main (argc, argv) int Argc; char ** argv; {struct in_addr sin_addr; int Retcode, i; char buf [32]; / * For server programs, it is often in unlimited The loop state, only when the user actively kill the process or system shutdown, it ends. For server programs that are forcibly terminated using Kill, because the main sleeve is not closed, the resources do not actively release, which may have an impact on the subsequent server program to restart. Therefore, the active closure of the main sleeve is a good habit. The following statement allows the program to perform the closemainsock () function to turn off the main sleeve, and then end the program when the program is received by signals such as Sigint, Sigquit, and Sigterm. Therefore, when you use KILL to terminate the server process, you should first use the KILL-2 PID to turn it off to close the main sleeve, and then override the process with Kill -9 PID. * / (void) Signal (Void) Signal (SIGQUIT, ClosemainSock); (Void) Signal (Sigterm, ClosemainSock); IF ((Retcode = InitPassiveSock ("TestService)) <0) {Printf "InitPassiveSock: Error Code =% D / N", Retcode); exit (-1);} while (1) {retcode = querySocketsmsg (); / * Query network message * / if (SockMsg.acceptnum == 1) { / * Is there a foreign connection waiting to receive? * / return = acceptconnection (& sin_addr); Printf ("Retcode =% D, IP =% S / N", retcode, inet_ntoa (sin_addr.s_addr));} else if (sockmsg.acceptnum == -1) / * master Socket error? * / printf ("Daemon sockets error./N"); for (i = 0; i I I ((Retcode = Recvpacket (SockMsg.Readqueue [i], buf, 32))> 0) Printf ("SOCKNO% D Recv String =% S / N ", SockMsg.ReadQueue [I], BUF); ELSE / * Return Data Length is zero, indicating connection interrupt, closing the socket. * / closeConnection (SockMsg.Readqueue [i]);}} / * end while * /} 2.5.4 Simple client example / * file name: client.c * / / * The client is executed, first initializes the data structure, and then waits for the user to enter the command.