Socket programming principle

zhaozj2021-02-08  284

Chapter II Socket Program Principle 2.1 The I / O command set of the problem of the problem is evolved from the command from Maltics and early systems, and its mode is open (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.

port

A communication port that can be named and addressed in the 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.

address

Two processes in communication in network communications are on 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, it takes three levels of addressing:

1. Some 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 process on each host 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.

Network byte order

Different computers store multi-byte value, some machines store low bytes (low price first) in the start address, some save high 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.

connection

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 summary, a three-way group can be used in the global unique mark in the network.

(Agreement, local address, local port number)

Such a three-way group called a half-association, which specifies every half of the connection.

Fully 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-way group to identify:

(Agreement, local address, local port number, remote address, far port number)

Such a five-way group called a association, that is, two protocols the same semi-correlation to combine 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 :

Connection (virtual circuit) or no connection

The connection service is an abstraction of the telephone system service mode, that is, each complete data transfer has to be established, using the 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 are given below:

Service type service example is a reliable message stream for connection

Reliable byte stream

Unreliable connection file transfer (FTP)

Remote login (Telnet)

Digital voice without connection with unreliable data report

Acknowledgment

Request - Answer Email (E-mail)

Registration letter in email

Network database query

order

In network transmission, two consecutive packets may pass different paths in terminal-end communication, so that the order of arrival destination may be different from the transmission. "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

Ensure that the application has no error in the data received. 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

Control the mechanism of the data transfer rate during the data transfer to ensure that the data is not lost. The TCP protocol provides this service. Byte stream

The byte flow means that only the packets in the transfer are regarded as a byte sequence, no boundaries of the data stream. The TCP protocol provides a word throttle service.

Packet

The recipient is to save the sender's packet boundary. The UDP protocol provides packet services.

Full duplex / half-duplex

The end-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 a proactive request method 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, which is willing to receive a customer request on a publicly recognized address (Zhou Zhi, such as FTP 21);

2. Wait for the customer 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 closed after the request is completed.

From the above described process:

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 receiving in the sequence of transmission. 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 report (SOCK_DGRAM)

A connectionless service is available. 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 lower layer protocols, 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 should 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. Its call format is as follows:

INT Pascal Far Bind (Socket S, Const Struct Sockaddr Far * Name, Int Namelen);

The parameter S is a socket descriptor (socket) that is returned and not connected by the socket () call. 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 order * /

Struct in_addr sin_addr; / * 32-bit IP address, network byte order * /

Char sin_zero [8]; / * Reserved * /

}

See 5.2.2 for a detailed description of bind ().

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 Struct Sockaddr Far * Name, INT Namelen);

The parameter S is a local socket descriptor that wants to establish a 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 the accept () is as follows:

Socket Pascal Far Accept (Socket S, Struct SockAddr Far * Addr, Int Far * Addrlen);

The parameter S is the local socket descriptor, which should be adjusted first before the parameters that use the Accept () call. 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 (), and its call format is as follows:

Int Pascal Far Listen (Socket S, INT Backlog);

The parameter S identifies a local established, unconnected socket, and the server is willing to receive requests from it. 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 () calls provide great convenience to implement concurrent services because it is necessary to return a new socket name whose typical structure is:

INT INITSOCKID, NewsockId;

IF ((INITSOCKID = Socket (....)) <0)

Error ("can't create socket";

IF (Bind (INITSOCKID, ....) <0)

Error ("Bind Error");

IF (Listen (INITSOCKID, 5) <0)

Error ("Listen Error");

For (;;) {

Newsockid = Accept (INITSOCKID, ...) / * block * /

NeWSockId <0)

Error ("accept error");

IF (fork () == 0) {/ * child process * /

CloseSocket (INITSOCKID);

Do (newsockid); / * Process request * /

exit (0);

}

CloseSocket (newsockid); / * Parent Process * /

}

The result of this program execution is that newsockID is related to the customer's socket. 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 repetition server, which is as follows:

INT INITSOCKID, NewsockId;

IF ((INITSOCKID = Socket (....)) <0)

Error ("can't create socket";

IF (Bind (INITSOCKID, ....) <0)

Error ("Bind Error");

IF (Listen (INITSOCKID, 5) <0)

Error ("Listen Error");

For (;;) {

Newsockid = Accept (INITSOCKID, ...) / * block * /

NeWSockId <0)

Error ("accept error");

Do (newsockid); / * Process request * /

CloseSocket (NewsockID);

}

Duplicate Server can only connect to a client in a time, which is repeated for multiple client processes, and is called repeated 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, and 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 specified by parameter s, and the format is as follows:

Int Pascal Far Recv (Socket S, CHAR FAR * BUF, INT LEN, INT FLAGS)

The parameter 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 CLOSESOCKET () call format 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 Call Processes For the preceding, 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, the system calls the timing The figure is as follows.

Socket system calls for connection-oriented protocols (such as TCP) are shown in Figure 2.1:

The server must start first until it executes accept () calls, 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 chart

Socket call without connection protocol is shown in Figure 2.2:

Figure 2. Skating timing diagram of the connectionless protocol

No connection server must also start first, otherwise the customer requests the 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 example uses the client / server mode for the connection protocol, which is shown in Figure 2.3:

Figure 2.3 Connected application flow chart

Server program:

/ * File name: streams.c * /

#include

#include

#define True 1

/ * This program establishes a socket and 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;

/ * Establish a socket * /

SOCK = Socket (AF_INET, SOCK_STREAM, 0);

IF (SOCK <0) {

Perror ("Opening Stream Socket";

Exit (1);

}

/ * Named socket using any port * /

Server.sin_family = af_INet;

Server.sin_port = incdr_any;

IF (Bind (STRUCK, STRUCKADDR *) & Server, SIZEOF (Server)) <0) {

PERROR ("Binding Stream Socket");

Exit (1);

}

/ * Find the specified port number and print it out * /

Length = Sizeof (Server);

IF (GetSockName (STRUCK, (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)

"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 connects to the sockets given by the command line; send it on the connection

A message, then turn off the socket. The format of the command line is: streamc hostname port number

The port number should be the same as the port number of the server program * /

Main (Argc, Argv)

Int argc;

Char * argv []; {

Int Sock;

Struct SockAddr_in Server;

Struct hostent * hp, * gethostByname ();

Char BUF [1024];

/ * Establish 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, SOCKADDR *) & Server, SIZEOF (Server)) <0) {

PERROR ("Connecting Stream Socket");

EXIT (3);

}

IF (SOND (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 and obtain the service 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 Name: Tcpsock.h * /

/ * Header file includes system header files that are often used by the socket program (which is given in this example is the header file under SCO UNIX. Other versions of UNIX may be slightly different, and define two of our own Data structure and its real algorical variables, and our function description. * / # include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

Typedef struct socketsmsg {/ * socket message structure * /

INT acceptnum; / * Indicates if there are foreign connections waiting to receive * /

INT readnum; / * There are external data waiting to read the number of connections * /

Int Readqueue [32]; / * There are external data waiting for the connection queue * /

INT WRITENUM; / * can send data to the number of connections * /

INT WriteQueue [32]; / * Accessories that can send data * /

INT exceptnum; / * has exceptions * /

INT Exceptqueue [32]; / * has exceptional connection queue * /

Socketsmsg;

Typedef struct sockets {/ * socket structure * /

INT daemonsock; / * Main cover * /

INT SOCKNUM; / * Data Socket Number * /

Int sockets [64]; / ​​* Data socket number * /

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 CRETECONNECTION (STRUCT IN_ADDR * SIN_ADDR);

INT AcceptConnection (Struct in_addr * ipaddr);

INT CloseConnection (Int Sockno);

Int querysocketsmsg ();

Int Sendpacket (int SockNo, Void * BUF, INT LEN)

INT Recvpacket (int SockNo, Void * buf, int size); 2.5.2 Function Source File / * File Name: Tcpsock.c * /

/ * This document gives the source code for nine functions, some of which give Chinese annotations * /

#include "tcpsock.h"

INT INITSOCKETSSTRUCT (CHAR * ServiceName)

/ * Initialize sockets structure. If succeeded the return 1, Else Return Error Code (<0) * /

/ * This function is used to only need a client-socket client, which is used to obtain service information. Service definition

In / etc / service file * /

{

Struct servent * servrec;

Struct SockAddr_in serv_addr;

IF ((servrec = getServByname (ServiceName, "TCP")) == NULL) {

Return (-1);

}

Bzero ((char *) & mysock, sizeof (sockets));

Mysock.port = servrec-> s_port; / * service port in network byte order * /

Return (1);

}

Int initpassiveesock (char * serviceename)

/ * Initialize passive socket. If succeed the return 1, Else Return Error Code (<0) * /

/ * This function is used to build a server program that requires a passive sleeve. In addition to obtaining service information, it is also established.

A passive sleeve. * /

{

INT MAINSOCK, FLAG = 1;

Struct servent * servrec;

Struct SockAddr_in serv_addr;

IF ((servrec = getServByname (ServiceName, "TCP")) == 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); / * Around any network interface * /

Serv_addr.sin_port = servrec-> s_port;

IF (Bind (MAINSOCK, STRUCT SOCKADDR *) & serv_addr, sizeof (serv_addr)) <0) {

Close (MAINSOCK);

Return (-3);

}

IF (Listen (MAINSOCK, 5) == -1) {/ * turn the active socket to passive socket, ready to receive 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); / * Stated the "readable" of the main cover * /

FD_SET (MAINSOCK, & MYSOCK.EXCEPTFDS); / * Decliminated an exception of the main cover * /

Return (1);

}

Void ClosemainSock ()

/ * Turn off the main sleeve and clear the declaration of the above event. Close the main sleeve before the end of the 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 indeicates 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 to establish a connection to the socket, and the connection is established to use the select () function to detect whether the socket is "writable" determine. * /

Close (tmpsock);

Return (-3); / * connection 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.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 (newsock);

Return (-4); / * Too Many Connections * /

}

Mysock.sockets [i] = news;

Mysock.socknum ;

Return (i);

}

INT CloseConnection (int Sockno)

/ * Close a connection indeformate by sockno. * /

{

int Retcode;

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 * /

{

IF ((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 * /

{

IF ((Mysock.Sockets [i]> 0) && (fd_isset (mysock.sockets [i], & efds)))

SockMsg.Exceptqueue [SockMsg.Exceptnum ] = i;

}

Return (retcode);

}

Int SendPacket (int Sockno, Void * BUF, INT LEN)

/ * Send a packet. If succeed return the number of send data, else return -1 * /

{

Int actlen;

IF ((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 receivers Data, Else IF the connection

Is Shutdown by Peer1 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 an infinite loop state, which only ends when the user actively kill the process or system shutdown. 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 (SIGINT, ClosemainSock);

(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? * /

Retcode = acceptconnection (& sin_addr);

Printf ("Retcode =% D, IP =% S / N", Retcode, INET_NTOA (SIN_ADDR.S_ADDR);

}

ELSE IF (SOCKMSG.ACCEPTNUM == -1) / * Main Skills Area error? * / Printf ("Daemon sockets error./N");

For (i = 0; i

IF ((RETCODE = Recvpacket (SockMsg.Readqueue [i], buf, 32))> 0)

Printf ("SOCKNO% D Recv String =% S / N", SockMsg.Readqueue [I], BUF);

ELSE / * Returns the length of zero, indicating the connection interrupt, closes the socket. * /

CloseConnection (SockMsg.Readqueue [i]);

}

} / * End while * /

}

2.5.4 Simple Customer Example / * File Name: Client.c * /

/ * The client is executed, first initializes the data structure, and then waits for the user to enter the command. It identifies four commands:

CONN (ECT): Connects to the server;

Send: Send data to the specified connection;

CLOS (E): Turn off the specified connection;

Quit: Exit the client.

* /

#include "tcpsock.h"

Main (Argc, Argv)

Int argc;

Char ** argv;

{

CHAR CMD_BUF [16];

Struct in_addr sin_addr;

Int sockno1, retcode;

Char * buf = "this is a string for test."

SIN_ADDR.S_ADDR = INET_ADDR ("166.111.5.249"); / * IP address of the host running server program * /

IF ((Retcode = INITSOCKETSSSSSSTRUCT ("TestService")) <0) {/ * initialized data structure * /

Printf ("INITSOCKETSSTRUCT: Error Code =% D / N", Retcode);

Exit (1);

}

While (1) {

Printf (">");

Gets (cmd_buf);

IF (! Strncmp (cmd_buf, "conn", 4)) {

Retcode = CREATECONNECTION (& SIN_ADDR); / * Establish a connection * /

Printf ("Return Code:% D / N", RETCODE);

}

Else if (! Strncmp (CMD_BUF, "Send", 4)) {

Printf ("Sockets Number:");

Scanf ("% d", & sockno1);

Retcode = sendpacket (Sockno1, BUF, 26); / * Send data * /

Printf ("Return Code:% D / N", Retcode, SizeOf (BUF));

}

Else IF (! Strncmp (CMD_BUF, "Close", 4)) {

Printf ("Sockets Number:");

Scanf ("% D", & sockno1); retcode = closeconnection (sockno1); / * Close connection * /

Printf ("Return Code:% D / N", RETCODE);

}

Else IF (! Strncmp (cmd_buf, "quit", 4))

exit (0);

Else

PUTCHAR ('/ 007');

} / * End while * /

}

转载请注明原文地址:https://www.9cbs.com/read-1682.html

New Post(0)