Xuyb email: bai_xy@21cn.com This article introduces the Socket programming common function usage method and the Socket programming general rules and client / server models to pay attention to the solution for the Socket programming commonly used function, and the solution to the symposium. Code instance. It is important to understand that the technical issues described herein requires readers to have a certain C language programming experience and basic knowledge in TCP / IP. To practice this example, you need to support the GCC compilation platform support under Linux. Socket Definition Network Socket Data Transfer is a special I / O, and Socket is also a file descriptor. Socket also has a function that is similar to the open file call - Socket (), which returns an integer Socket descriptor, subsequent connection creation, data transfer operations are achieved by this socket. Common Socket types There are two types: stream socket-socket_stream and dataset socket-sock_dgram. Flow is a connection-oriented socket, targeting a connected TCP service application; Data reported Socket is an unconnected socket, corresponding to a connected UDP service application. Socket Programming Related Data Type Define Computer Data Storage There are two bytes priority sequence: high byte priority and low byte priority. The INTENET data is transmitted on the network in a high byte priority order, so it is necessary to transfer data on the Internet in the Internet in the Internet. The first structural type we have to discuss is: struct sockaddr, this type is used to save Socket information: struct sockaddr {unsigned short sa_family; / * address family, AF_XXX * / CHAR SA_DATA [14]; / * 14 bytes The protocol address * /}; sa_family is generally AF_INET; SA_DATA contains the IP address and port number of the socket. There is also a structural type: strunt sockaddr_in {short int sin_family; / * address family * / unsigned short int sin_port; / * port number * / struct in_addr sin_addr; / * ip address * / unsigned char sin_zero [8]; / * Fill 0 to keep the same size * /} with Struct SockAddr; this structure is more convenient. SIN_ZERO (which is used to fill the SockAddr_in structure to the same length as Struct SockAddr) should be placed zero with the Bzero () or MEMSET () function. Poinpectdddr_in's pointer and pointer to SockAddr can be converted to each other, which means that if the parameter type required for a function is SockAddr, you can convert a pointer to the SockAddr_in when the function call is called to point to the socketdr pointer; or the opposite. SIN_FAMILY is usually assigned AF_INET; in_port and sin_addr should be converted into network byte priority; while SIN_ADDR does not require conversion.
Let's discuss several byte sequential conversion functions: htons () - "Host to NetWork Short"; htonl () - "Host to NetWork Long" () - "NetWork to Host Short"; NTOHL () - - "NetWork to Host Long" Here, h indicates "host", n means "network", and s represents "short", and L means "long". Open the Socket descriptor, establish the binding and establish the connection socket function original to: int Socket (int Domain, int type, int protocol); Domain parameter specifies the type of socket: SOCK_STREAM or SOCK_DGRAM; protocol usually assigns "0". Socket () call returns an integer socket descriptor that you can use it later. Once returns a Socket descriptor via the Socket call, you should associate this socket with a port on you (often call the function when you design the server-side program. You can then listen to the service request at the port And the client generally does not need to call the function). Bind function original is: int bind (int Sockfd, intectRlen); sockfd is a socket descriptor, my_addr is a pointer to the SockAddr type that contains information such as the native IP address and port number; AddRlen It is set to sizeof (struct sockaddr). Finally, the point to the bind function is that you can automatically get the native IP address and randomly get a port number without the occupied port number: my_addr.sin_port = 0; / * System randomly selects one unused Port number * / my_addr.sin_addr.s_addr = INADDR_Addr; / * Fill in the native IP address * / by setting my_addr.sin_port, the function will automatically select an unidentified port to use. Similarly, by placing my_addr.sin_addr.s_addr is set to INADDR_Any, the system will automatically fill in the native IP address. The bind () function returns 0 when successfully called; return "-1" when an error is encountered and set Errno to the corresponding error number. Also note that when the modulation function is usually, do not set the port number to a value of less than 1024, since 1 to 1024 are reserved port numbers, and you can use any of the port numbers that are not occupied in 1024. The Connect () function is used to create a TCP connection with the remote server, its function prototype: int connection_addr, int addrlen; sockfd is the Sockt descriptor of the destination server; serv_addr is an IP containing the desertive machine IP The address and port number of the port number. Returns -1 when an error is encountered, and the corresponding error code is included in Errno. The client program design does not need to be called bind (), because in this case, you only need to know the IP address of the machine, and which port is connected to the server and the server does not need to care, the kernel will automatically select an unbeatable port for The client is used.
Listen () - Monitoring Is Service Request In the server-side program, after the Socket is bundled with a certain port, you need to listen to the port to process the service request arriving. INT Listen (int Sockfd, int Backlog); SockFD is the socket descriptor returned by the socket system call; Backlog specifies the maximum number of requests allowed in the request queue, and the access request will wait for Accept () in the queue (hereinafter) . Cklog limits the number of requests for the wait for services in the queue, and most system defaults to 20. Returns -1 when Listen encounters an error, Errno is set to the corresponding error code. Therefore, the server-side program typically performs function calls in the following order: socket (); bind (); listen (); / * accept () goes here * / accept () - Connect the service request for the port. This connection request will queue the waiter Accept () when a client attempts to connect with the server listening to the server. The Accept () function returns a new Socket descriptor to be used for this new connection by calling the accept () function. The server can continue to listen over the previous socket, while the data Send () (send) and RECV () operation can be performed on the new Socket descriptor: int access (int sockfd, void * addr, int *) AddRlen; SockFD is the listened socket descriptor, and addr is usually a pointer to the SockAddr_in variable, which is used to store the information of the host of the connection request service (a host issues the request from a port); AddRTEN usually For a pointer variable pointing value of sizeof (struct socaddr_in). Error returns one -1 and sets the corresponding errno value. Send () and RECV () - Data Transmission These two functions are data transmission on the connection-oriented Socket. Send () function original is: int send (int Len, int flags); SOCKFD is the socket descriptor you want to transfer data, and the MSG is a pointer to the data to send. Len is the length of data in bytes. Flags typically be set to 0 (with the usage of this parameter can be referred to the Man Manual). Char * msg = "Beej Was Here!"; int LEN, BYTES_SENT; ... ... LEN = Strlen (MSG); Bytes_Sent = Send (Sockfd, MSG, Len, 0); ... ... Send ) The function returns the number of bytes actually sent, which may be less than the data you want to send. So you need to measure the return value of Send (). This situation should be processed when the send () return value is mismatched with the LEN. The RECV () function original is: int RECV (int LEN, Unsigned INT FLAGS); SockFD is a socket descriptor that accepts data; BUF is a buffer that stores receiving data; LEN is the length of the buffer. Flags is also set to 0. RECV () returns the number of bytes that is actually received, or when an error occurs, returns the corresponding Errno value.
Sendto () and RECVFROM () - Data transfer in the data report in the unconnected datagram socket mode, because the local socket does not establish a connection with the remote machine, the address should be specified when sending data, Sendto ( The function original is: int sendto (int Len, unsigned int flag, const struct socöta); INT tolen; the function is more than two parameters than the send () function, TO means the style The IP address and port number information are often assigned to SizeOf (Struct SockAddr). The SendTo function also returns the actual data byte length or returns -1 when an error occurs. The RECVFROM () function is: int RECVFROM (int LEN, Unsigned Int Lags, Struct SockAddr * from, INT * FROMLEN); FromLomlen is a Struct SockAddr type variable, the variable holds the IP of the source Address and port number. Fromlen is always set to SIZEOF (Struct SockAddr). When Recvfrom () returns, the fromLOM contains the number of data bytes actually stored in the FROM. The RECVFROM () function returns the number of bytes received or returns -1 when an error occurs, and the corresponding errno is copied. It should be noted that when you call the connect () function for the duplicate Socket, you can use Send () and RECV () to perform data transfer, but the socket is still a datagram, and uses the UDP of the transport layer. service. However, when sending or receiving a data report, the kernel will automatically add the purpose and source address information. Close () and ShutDown () - End Data Transfer When all data operations are completed, you can call the Close () function to release the socket, stop any data operation on the socket: Close (SockFD); you also You can call the shutdown () function to close the socket. This function allows you to only stop data transmission in a certain direction, while the data transfer in one direction continues. If you can turn off a Socket write operation, you can continue to accept data on the socket until all data is read. INT Shutdown (int suckfd, int how); SockFD's meaning is obvious, and parameter how can be set to the following: · 0 ------- Do not allow continued reception data · 1 ------- Allows to send data · 2 ------- Do not allow continued transmission and reception data, all call close () shutdown returns 0 when the operation is successful, return -1 (parallel corresponding errno) when an error occurs . DNS - Domain Name Service Related Functions Since the IP address is difficult to remember and read, in order to read and write memories, people often use domain names to represent the host, which requires the conversion of domain names and IP addresses.
The function gethostByname () is to complete this conversion, the function original is: struct hostent * gethostbyname (const char * name); function returns a structure type called Hosten, which is defined as follows: struct hostent {char * h_name; / * The official domain name of the host * / char ** h_aliases; / * An address type returned by the host other name * / int h_addrtype; / * ending with NULL, an AF-INET * / INT H_LENGTH; / * address in the Internet environment Byte length * / char ** h_addr_list; / * An array ends with 0, including all addresses of the host * /}; #define h_addr h_addr_list [0] / * The first in H-Addr-list Address * / When the gethostname () calls successfully, return to the pointer to the Struct Hosten, returns -1 when the call fails. When calling gethostByName, you cannot use the PERROR () function to output an error message, and you should use the HERROR () function to output. Connected Customer / Server Code Instance This server sends a string "Hello, World!" Via a connection. Just run the server software on the server, run the customer software on the client, and the client will receive the string.
The server software code see the program 1: #include stdio.h #include stdlib.h #include errno.h #includes string.h #include sys / types.h #include netinet / in.h #include sys / socket.h # Include sys / wait.h #define myport 3490 / * server listening port number * / #define backlog 10 / * maximum connection request number * / main () {INTSOCK FD, new_fd; / * listener Socket: SOCK_FD, Data Transfer Socket : new_fd * / struct socmeddr_in my_addr; / * Native address information * / struct soc design * / struct sockaddr_in their_addr; / * Customer address information * / int SIN_SIZE; IF ((SockFD = socket (AF_INET, SOCK_STREAM, 0)) == -1) { / * Error detection * / perror ("socket"); exit (1);} my_addr.sin_family = AF_INET; my_addr.sin_port = htons (myport); my_addr.sin_addr.s_addr = INADDR_ADR_ADDR = INADDR_ANY; BZERO (& (my_addr.sin_zero) , 8); if (bind (STRUCKD, STRUCKADDR *) & my_addr, sizeof (Struct SockAddr)) == -1) {/ * Error detection * / perror ("bind"); exit (1);} if Listen (SockFD, Backlog) == -1) {/ * Error detection * / perror ("listen"); exit (1);} while (1) { / * Main accept () loop * / sin_size = sizeof (struct sockaddr_in); if ((new_fd = accept (sockfd, (struct sockaddr *) & their_addr, & sin_size)) == -1) {perror ( "accept"); continue } Printf ("Server: Got Connection FROM% S", INET_NTOA (THEIR_ADDR.SIN_ADDR)); if (! Fork ()) {/ * child process code segment * / if (send (new_fd, "hello, world!" , 14, 0) == -1) PERROR ("send"); close (new_fd); exit (0);} close (new_fd); / * The parent process no longer needs this socket * / waitpid (-1, null , Wnohang> 0 / * Wait for the sub-process, clear subscriptions of the sub-process * /}} (program 1) The server first creates a socket,
The Socket is then bundled with the local address / port number. After success, listen to the corresponding socket, generate a new Socket when the Accpet captures a connection service request, and send characters to the client through this new Socket String "Hello, World!", Then close the socket. The Fork () function generates a child process to handle the data transfer section, the value of the pay () statement is 0 for the child. Therefore, if the IF statement containing the Fork function is the sub-process code section, which is executed concurrently with the parent process part behind the IF statement.
Client Software Code Substrate 2: # includestdio.h #include stdlib.h #include errno.h #include string.h #include netdb.h #include sys / types.h #include netinet / in.h #include sys /socket.h #define port 3490 #define maxDataSize 100 / * Each maximum data transmission * / int main (int Argc, char * argv []) {int Sockfd, NumBytes; char buf [maxDataSize]; struct hostent * HE Struct SockAddr_in their_addr; if (argc! = 2) {fprintf (stderr, "usage: client hostname); exit (1);} if ((he = gethostbyname) == null) {Herror ("gethostbyname"); exit (1);} IF ((Sockfd = socket (AF_INET, SOCK_STREAM, 0) == -1) {Perror ("socket"); exit (1);} their_addr.sin_family = AF_IT Their_addr.sin_port = htons (port); their_addr.sin_addr = * (Struct in_addr *) HE-> h_addr); Bzero (& (THEIR_ADDR.SIN_ZERO, 8); if (connect (Sockfd, (Struct SockAddr *) & THEIR_ADDR, SIZEOF (STRUCT SOCKADDR)) == -1) {/ * Error detection * / PERROR ("Connect"); exit (1);} if ((NumBytes = Recv (Sockfd, BUF, MaxDataSize, 0) == -1) {Perror ("RECV"); exit (1);} buf [ NumBytes] = ''; Printf ("Received:% S", BUF); Close (SockFD); Return 0;} (Program 2) The client code is relatively simple, first get its IP address through the server domain name, Then create a socket, call the CONNECT function to establish a connection with the server, and receive the data sent from the server after the connection is successful, and finally closes the socket, and end the program. The unconnected client / server program is the same in principle and the connected client / server, the difference between the two is that customers in the connectionless client / server generally does not need to establish a connection, and when sending data, it is required to be specified. The address of the remote machine.
What happens when the Blocking concept and select () function When the server is running to the Accept statement, what happens without the customer connection service? At this time, the server will stop waiting for the arrival request from the accept statement. Similarly, when the program runs to the received data statement, if there is no data to be read, the program is also stopped on the receiving statement. This is called blocking. But if you want the server only pay only if there is a customer waiting to be connected, you will accept the connection; otherwise you continue to do other things, you can implement the Socke is set to non-blocking mode: Non-blocking Socket does not have a customer waiting Return the Accept call immediately. #include unistd.h #include fcntl.h...; sockfd = socket (AF_INET, SOCK_STREAM, 0); FCNTL (SOCKFD, F_SETFL, O_NONBLOCK);........................................... "Polling" Some sockets. When an attempt is read from a non-blocking socket that does not have data waiting to be processed, the function will return immediately, and the return value is set to -1, and errno is set to Ewouldblock. But this "polling" will make the CPU is busy waiting, reducing performance. Considering this situation, assuming that you want the server to read data from the established connection while listening to the connection service request, you may think of an Accept statement and multiple RECV () statements, but because accept and recv are blocked So this idea will not succeed. Calling non-blocking sockets will greatly waste system resources. And calling Select () will effectively solve this problem, which allows you to hang up the process itself, while simultaneously monitor any activity of the required group of file descriptors, just confirm that in any monitored file descriptor Activity, select () call will return to the information that is ready for the file descriptor, thereby achieving random changes for the process, without having to test the input by the process itself and wasting the CPU overhead. SELECT function original is: int SELECT (int Numfds, fd_set * readfds, fd_set * writefds, fd_set * exec *; Struct Timeval * timeout); where readfds, writefds, ExcePTFDs are read, write and exceptions for SELECT () Document descriptor collection. If you want to determine if you can read data from a standard input and a socket descriptor, you only need to add the standard input file descriptor 0 and the corresponding SockDTFD to the READFDS collection; Numfds's value is the highest number of numbers. Document descriptor plus 1, the value of Numfds should be SOCKFD 1 in this example; the readfds will be modified, indicating that a file descriptor is ready to be read, you can test it via fd_issset (). In order to implement the setting, reset, and test of the corresponding file descriptor in the fd_set, it provides a set of macros: fd_zero (fd_set * set) ---- Clear a file descriptor set; fd_set (int FD, FD_SET * SET) - --- Add a file descriptor to the file descriptor set; fd_clr (int FD, fd_set * set) ---- Clear a file descriptor from the file descriptor; fd_isset (int FD, FD_SET * SET) - - Test whether the file descriptor is set.