BEEJ Network Socket Program Guide

zhaozj2021-02-08  321

BEEJ Network Socket Program Guide

[Version 1.5.5]

[http://www.ecst.csuchico.idu/~beej/guide/net/]

Introduction Socket program makes you frustrated? Is it a useful information from MAN PAGES? You want to keep up with the Internet-related procedure, but don't know what the structure before calling connect (). Wait ... I have done these things, I will share my knowledge with everyone. If you understand the C language and want to cross the web programming swamp, then you come to the place.

The reader object is a guide, not a reference book. If you start Socket programming and want to find a book, then you are my reader. But this is not a full Socket programming.

Most of the code in this document is successfully compiled with GNU's GCC on the Linux platform PC. And they have successfully compiled with GCC on the HPUX platform. But attention is not to be tested independently of each code snippet.

Directory: 1) What is a socket? 2) Two types of Internet sockets 3) Network theory 4) Structural 5) Native conversion 6) IP address and how to handle them 7) Socket () function 8) BIND () function 9) Connect () function 10 () function 11) accept () function 12) send () and RECV () functions 13) Sendto () and Recvfrom () Functions 14) Close () and shutdown () function 15) getPeerName () function 16) gethostname () Function 17) Domain Name Service (DNS) 18) Customer - Server Background Knowledge 19) Simple Server 20) Simple Client 21) Data Supply Set Socket22) Block 23) SELECT () - Multi-Synchronization I / O24) Reference

What is Socket?

You often hear people talk about "socket", maybe you still don't know its exact meaning. Now let me tell you: it is a way to communicate with the standard UNIX file descriptor and other program.

what?

You may hear some UNIX masters (Hacker) said: "Yeah, everything in UNIX is the file!" The guy may be talking about a fact: When performing any form of I / O, the UNIX program is reading Or write a file descriptor. A file descriptor is just an integer associated with an open file. But (note that later), this file may be a network connection, FIFO, pipeline, terminal, file on disk or what other things. All things in UNIX are files! So, when you want to communicate with other programs on the Internet, you will use the file descriptor. You must understand what you just have just now. Now your mind may take this idea: "So where I get the file descriptor of the network communication?" Socket Descriptor, then you will pass it again for Send () and RECV () calls.

"But ...", you may have a lot of doubts, "If it is a file descriptor, why don't you usually call read () and write () to connect communication communication?" Simple answer is: "You can use!". The detailed answer is: "You can, but using Send () and RECV () let you better control data transfer."

There is such a situation: in our world, there are many sockets. There is a Darpa Internet address (Internet Socket), the path name of the local node (UNIX socket), CCITT X.25 address (you can completely ignore the X.25 socket). Perhaps there are other things on your UNIX machine. We are here only the first one: Internet socket. Two types of Internet sockets

What do you mean? Have two types of Internet sockets? Yes. No, I am lying. There is also a lot, but I don't want to be scared you. We only talk about two. In addition to this, I plan to introduce "Raw Sockets" is also very powerful, it is worth seeing.

So what is these two types? One is "stream sockets" (stream format), and the other is "DataGram Sockets" (packet format). We will also use "Sock_Stream" and "SOCK_DGRAM" in the future. Data reports are sometimes called "no connection socket" (if you do to connect, you can use connect ().) Stream socket is a data stream of reliable two-way communication. If you output "1, 2" in order to the socket, they will reach the other side by order "1, 2". They are not erroneous, have their own error control, here no discussion.

What is in using a streaming socket? You may have heard Telnet, isn't it? It uses stream sockets. You need the characters you entered in order, isn't it? Similarly, the HTTP protocol used by the WWW browser also uses them to download the page. In fact, when you pass the port 80 Telnet to a WWW site, then enter "get PageName", you can also get the content of HTML. Why can stream socket meet high quality data transfer? This is because it uses the "TCP" "," also is called "TCP" (see RFC-793 to get detail.) TCP controls your data to reach in order and not wrong

error. You may hear "TCP" because he heard "TCP / IP". The IP here refers to the "Internet Protocol" (see RFC-791.) IP just processes Internet routes.

So what is the data settlement? Why is it called no connection? Why is it unreliable? There are some facts: If you send a datagram, it may arrive, it may order. If it arrives, there is no error in the inside of this package. Denual newspapers also use IP routing, but it does not use TCP. It uses the "User DataGram Protocol", is also called "UDP" (see RFC-768.)

Why are they not connected? Mainly because it maintains a connection as it is not like a stream socket. Just create a package, construct a IP header with target information, and then send it out. No need to connect. They are usually used for transmission packages - package information. Simple applications include: TFTP, BootP, etc.

You may think: "If the data lost these programs normal work?" My friend, each program has its own agreement on UDP. For example, the TFTP protocol has been accepted by a package, and the receiver must send back a bag to say "I received it!" (A "command correctly responded" is also called "ACK" package). If within a certain period of time (eg 5 seconds), the sender does not receive a response, which will be resent until ACK is obtained. This ACK process is very important when implementing the SOCK_DGRAM application. Since the network theory I just mentioned the protocol layer, now how to discuss how the network works and some examples of how the SOCK_DGRAM package is established. Of course, you can also skip this paragraph, if you think it is already familiar. It's time to learn data packages (Data Encapsulation)! It is very important. Its importance is important to your online courses (Figure 1: Data Package) in the study, no matter how you have to master it. The main content is: a package, first by the first protocol (here is TFTP) in its header ("package"), then, the entire data (including TFTP header) is another protocol (Here is UDP) package, then the next (IP), repeatedly, until the hardware (physical) layer (here is an Ethernet). When another machine receives the package, the hardware strips the Ethernet head, the kernel strips IP and UDP head, and the TFTP program will peel the TFTP header, and finally obtain the data. Now we finally talked about the name of the phrase a network layering model (Layered NetWork Model). This network model has many advantages to the relative other models on the network system. For example, you can write a socket program without the physical transmission of data (serial port, Ethernet, connection unit interface (AUI) or other media), because the underlying program will handle them. The actual network hardware and topology are transparent for programmers. I don't say other nonsense, I now list the entire hierarchy model. If you have to participate in the network exam, you must remember: Application Layer (Application) Conference Layer (SESSION) Transport Network Layer (DATA LINK) Physical Layer (Physical The physical layer is hardware (serial port, Ethernet, etc.). The application layer is the farthest separation of hardware layers - it is where users and network interactions. This model is so common, if you want, you can use it as a car guide. Putting it to UNIX, the result is an Application Layer (Telnet, FTP, etc.) (TELNET, FTP, etc.) (TCP, UDP) Internet Layer (IP and Routing) Network Access Layer (Network Access Layer) now, you may see how these hierarchy coordinate to encapsulate the original data. See how much work is to build a simple packet? Oops, you will have to use "cat" to create a data package! This is just a joke. For streaming sockets, you want to do Send () sends data. For datagram socket, you encapsulate data according to your choice, and use Sendto (). The kernel will establish a transport layer and the Internet layer, and the hardware completes the network access layer. This is modern technology. Now end our network theory speed class. Oh, forget about telling you about the route. But I am not ready to talk about it. If you really care, please refer to IP RFC.

Structural body

Finally talked about programming. In this chapter, I will talk about the types of data used by the socket. Because some of them is very important. The first is a simple one: socket descriptor. It is the following type:

int

Just a common int.

From now on, things become incredible, and what you need to do is to continue. Note this fact: There are two types of bytes: important bytes (sometimes called "OcTet", ie, eight-bit groups) in front, or unimportant bytes in front. The previous called "Network Byte Order". Some machines are internally stored in this order, while others are otherwise. When I say a data must be in the Nbo order, then you want to call a function (such as htons ()) to convert it from the host byte order. If I didn't mention NBO, then let it keep the unit sequence.

My first structure (in this technical manual TM) - struct sockaddr.. This structure is a multi-type socket storage socket address information:

Struct sockaddr {

UNSIGNED SHORT SA_FAMILY; / * Address Family, AF_XXX * /

Char SA_DATA [14]; / ​​* 14 byte protocol address * /

}

SA_FAMILY can be a variety of types, but in this article "AF_INET". SA_DATA contains target addresses and port information in the socket. It seems that it is a bit unused.

In order to deal with Struct SockAddr, the programmer creates a parallel structure: struct sockaddr_in ("in" represents "Internet".)

Struct sockaddr_in {

Short int sin_family; / * Communication type * /

Unsigned short int sin_port; / * port * /

Struct in_addr sin_addr; / * Internet Address * /

Unsigned char sin_zero [8]; / * The same length as the SockAddr structure * /

}

Use this data structure to easily process the basic elements of the socket address. Note SIN_ZERO (which is added to this structure, and the length and Struct SockAddr) should use function Bzero () or MEMSET () to settle all zero. At the same time, this important byte, a pointer to the SockAddr_in structure can also be directed to the structure SockAddr and replaced it. In this way, even if Socket () wants Struct SockAddr *, you can still use Struct SockAddr_in and in the final conversion. At the same time, pay attention to the SA_FAMILY in SIN_FAMILY and STRUCT SOCKADDR and can be set to "AF_INET". Finally, sin_port and sin_addr must be a network byte order (NetWork Byte Order)!

You may be against: "But how do you make the entire data structure Struct in_addr sin_addr follow the network byte order?" To know the answer to this question, we have to take a look at this data structure: struct in_addr, there is such a UnionS:

/ * Internet address (a structure related to history) * /

Struct in_addr {

Unsigned long s_addr;

}

It used to be a worst union, but now those days have passed. If you declare "INA" is an instance of the data structure struct sockaddr_in, "INA.SIN_ADDR.S_ADDR" stores 4 bytes of IP addresses (using network byte order). If your unfortunate system is used or terrible combined with Struct in_addr, you can rest assured that 4 bytes of IP addresses and the same as I said above (this is because "#define" is used.) Native conversion

We now go to the new chapter. We have talked a lot of networks to the transformation of the native byte, and now you can practice!

You can convert two types: short (two bytes) and long (four bytes). This function is also available for variable type unsigned. Suppose you want to turn the SHORT from this unit byte sequence to network byte order. Use "H" to "host", then "TO", then use "N" to "NetWork", and finally use "S" to indicate "SHORT": h-to-ns, or htons () "Host to NetWork Short").

too easy...

If it is not too stupid, you must think of the correct combination of "n", "h", "s", and "l", such as the "Short to Long Host" function, not only There is nothing here, all occasions are not. But here is:

Htons () - "Host to NetWork Short"

HTONL () - "Host to NetWork Long"

NTOHS () - "NetWork to Host Short"

NTOHL () - "NetWork to Host Long"

Now you may think you already know them. You may also think: "What should I do if I want to change the order of char?" But you may think about it immediately, "don't think about it". You may think that my 68000 machine has used the network byte order, I don't have to call HTONL () conversion IP addresses. You may be right, but when you transplant your program to other machines, your program will fail. portability! Here is UNIX world! Remember: When you put the data on the network, it is sure that they are network byte order.

Last: Why is SIN_ADDR and SIN_PORT in the data structure Struct SockAddr_in, and SIN_FAMIL is not needed in SIN_FAMILY: SIN_ADDR and SIN_PORT are encapsulated in the package IP and UDP layers. Therefore, they must be a network byte order. However, the SIN_FAMILY domain is only used by the kernel to determine what type of address is included in the data structure, so it must be the unit of this byte. At the same time, SIN_FAMILY is not sent to the network, which can be the native byte order.

IP address and how to handle them

Now we are fortunate, because we have a lot of functions to make it easy to operate the IP address. There is no need to calculate them with manually, and there is no need to store the growth typing type with "<<" operation. First of all, suppose you already have a SockAddr_in structure INA, you have an IP address "132.241.5.10" to store it, you will use the function inet_addr (), convert the IP address from the point format to unsigned long finishing type. The method is as follows: INA.SIN_ADDR.S_ADDR = INET_ADDR ("132.241.5.10");

Note that the address returned in inet_addr () is already a network byte format, so you don't need to call the function htonl ().

We now find that the above code snippet is not very complete because it has no error check. Obviously, returns -1 when inet_addr () occurs. Remember these binary numbers? (Number of no characters) - 1 is only in line with IP address 255.255.255.255! This is a broadcast address! Big wrong! Remember to make an error check.

Ok, now you can convert the IP address to growth. Is there any way it? Can it output an IN_ADDR structure to a point format? In this case, you will use the function inet_ntoa () ("NTOA" meaning is "NetWork to ASCII"), just like this:

Printf ("% s", inet_ntoa (INA.SIN_ADDR);

It will output an IP address. It should be noted that INT_NTOA () uses structural in-addr as a parameter, not a long shape. It is also important to note that it returns a pointer to a character. It is a static fixed pointer controlled by inet_ntoa (), so each time you call inet_ntoa (), it will overwrite the IP address from the last call. E.g:

Char * a1, * a2;

.

.

A1 = INET_NTOA (INA1.SIN_ADDR); / * This is 198.92.129.1 * /

A2 = INET_NTOA (INA2.SIN_ADDR); / * This is 132.241.5.10 * /

Printf ("Address 1:% S / N", A1);

Printf ("Address 2:% S / N", A2);

The output is as follows:

Address 1: 132.241.5.10

Address 2: 132.241.5.10

If you need to save this IP address, use the stropy () function to point to your own character pointer.

The above is the introduction of this topic. Later, you will learn a string of "Wintehouse.gov" to convert a string itself into the IP address it (see domain name service, after later).

Socket () function

I think I can't mention this anymore - I will discuss the socket () system call.

Here is a detailed introduction:

#include

#include

INT Socket (int Domain, int type, int protocol);

But what is their parameters? First, Domain should be set to "AF_INET", just like the data structure Struct SockAddr_in above. Then, the parameter TYPE tells the kernel to be a SOCK_STREAM type or a SOCK_DGRAM type. Finally, set Protocol to "0". (Note: There are many kind of Domain, Type, I can't list one by one, please see Socket () MAN Help. Of course, there is a "better" way to get protocol. Please check with getProtobyname () MAN Help.) Socket () just returns the Socket descriptor that may be used in the system call species, or returns -1 when the error is wrong. The returned error value will be stored in the global variable errno. (Please refer to the Man Help of Perror ().)

Bind () function

Once you have a socket, you may have to associate the socket and a certain port on the machine. (If you want to use listen () to listen to the data of a certain port, this is a step -Mud tell you to tell you "Telnet X.y.z 6969".) If you just want to use connect (), then this step is not necessary. But in any case, please continue to read.

Here is the system call BIND ():

#include

#include

INT Bind (int Sockfd, struct sockaddr * my_addr, int addrlen);

Sockfd is the file descriptor that calls socket. My_addr is a pointer to the data structure Struct SockAddr, which saves your address (ie port and IP address) information. Addrlen is set to SizeOf (Struct SockAddr).

Isn't it very much? Let's take a look at the example:

#include

#include

#include

#define myport 3490

Main ()

{

Int sockfd;

Struct SockAddr_in my_addr;

Sockfd = socket (AF_INET, SOCK_STREAM, 0); / * Requires Error Check * /

MY_ADDR.SIN_FAMILY = AF_INET; / * HOST BYTE ORDER * /

My_addr.sin_port = htons (myport); / * short, network byte order * /

MY_ADDR.SIN_ADDR.S_ADDR = INET_ADDR ("132.241.5.10");

Bzero (& (my_addr.sin_zero) ,; / * Zero the rest of the struct * /

/ * Don't forget your error checking for bind (): * /

Bind (SockFD, Struct SockAddr *) & my_addr, sizeof (struct sockaddr);

.

.

.

There are also a few things to pay attention to here. My_addr.sin_port is the network byte order, MY_ADDR.SIN_ADDR.S_ADDR is also. Another thing to note is that the system is different, and the included header files are also different. Please check the local Man Help file.

The last thing to say in the Bind () topic is that some work can be processed automatically when handling your IP address and / or port. MY_ADDR.SIN_PORT = 0; / * Randomly select a portless port * /

MY_ADDR.SIN_ADDR.S_ADDR = INADDR_Anddr_Anddr; / * Use your IP address * /

You tell Bind () to select the appropriate port by assigning 0 to my_addr.sin_port. Similarly, set my_addr.sin_addr.s_addr to INADDR_any, tell it to automatically fill in the IP address of the machine it run.

If you have always been cautious, then you may notice that I didn't switch the INADDR_Any to the network byte order! This is because I know the internal thing: INADDR_ANY is actually 0! Even if you change the order of the byte, 0 is still 0. But the perfectionist said that it should be everywhere, INADDR_ANY may be 12? You can't work your code, then look at the following code:

MY_ADDR.SIN_PORT = HTONS (0); / * Randomly select a portless port * /

MY_ADDR.SIN_ADDR.S_ADDR = HTONL (INADDR_ANY); / * Using your own IP address * /

You may not believe that the above code will be able to port. I just want to point out that since the programs you have encountered will not run the INAddr_any using HTONL.

Bind () is still returning -1 when the error is wrong, and the global error variable errno is set.

When you call Bind (), another thing you have to be careful is: Do not use a port number of less than 1024. All port numbers less than 1024 are all retained! You can choose the port from 1024 to 65535 (if they are not used by other programs).

Another little thing you have to pay is: Sometimes you don't need to call it at all. If you communicate with the remote machine with the remote machine, you don't need to care about your local port number (just when you use Telnet), you can check the connect connect (), it will check the tape Words are bound to port, if not, it will bind one of the local ports that are not used.

Connect () program

Now we assume that you are a Telnet program. Your user commands you get the file descriptor of the socket. You listen to the command call socket (). Next, your user tells you to "132.241.5.10" through port 23 (standard Telnet port). What should you do? Lucky is, you are reading connect () - how to connect to the remote host. You don't want your user to disappoint.

Connect () system call is like this:

#include

#include

INT Connect (int sockfd, struct sockaddr * serv_addr, int addrlen);

Sockfd is a socket file descriptor that the system calls socket () returns. Serv_addr is a data structure Struct SockAddr that holds the destination port and IP address. Addrlen is set to SizeOf (Struct SockAddr).

Want to know more? Let us look at an example:

#include

#include

#include

#define dest_ip "132.241.5.10"

#define dest_port 23

MAIN () {

Int sockfd;

Struct SockAddr_in dest_addr; / * Destination Address * /

Sockfd = Socket (AF_INET, SOCK_STREAM, 0); / * Error Check * /

DEST_ADDR.SIN_FAMILY = AF_INET; / * HOST BYTE ORDER * /

DEST_ADDR.SIN_PORT = HTONS (dest_port); / * Short, Network Byte Order * /

DEST_ADDR.SIN_ADDR.S_ADDR = INET_ADDR (DEST_IP);

Bzero (& (dest_addr.sin_zero) ,; / * zero the rest of the structure * /

/ * Don't forget to error check the connection ()! * /

Connect (Sockfd, Struct Sockaddr *) & Dest_addr, SizeOf (Struct SockAddr);

.

.

.

Once again, you should check the return value of Connect () - it returns -1 when the error is wrong, and set the global error variable errno.

At the same time, you may see that I didn't call bind (). Because I don't care about the local port number. I only care about me to go there. The kernel will choose a suitable port number for me, and the local information we are also automatically obtained. Everything don't worry.

Listen () function

It is time to change the content. If you don't want to connect with a remote address, or say it is only to kick it, then you need to wait for access request and use various methods to handle them. Process is divided into two steps: First, you listen - Listen (), then you accept --Accept () (see below).

In addition to a little explanation, the system calls Listen is also quite simple.

INT Listen (int Sockfd, int backlog);

Sockfd is a socket file descriptor that calls socket (). BACKLOG is the number of connections allowed in the entered queue. What do you mean? The entry connection is waiting in the queue until you accept (Accept () Please see the following article) The number of their number is limited to the allowable queue. Most systems allow the number of allowed 20, you can also set to 5 to 10.

Like other functions, return -1 when an error occurs, and set global error variable errno.

You may imagine, you or call bind () before you call listen () or let the kernel choose a port. If you want to listen to the entered connection, the order in which the system call can be like this:

Socket ();

Bind ();

Listen ();

/ * accept () should be here * /

Because it is quite clear, I will not give an example here. (The code in the chapter of Accept () will be more complete.) The truly trouble part is in Accept ().

ACCEPT () function

Ready, system call accept () will be a bit weird place! You can imagine something that happens: Some people have passed a port connection from the Listen ()) from a far away place (Connect ()) to your machine. Its connection will be added to the queue waiting for acceptance (Accept ()). You call accept () tells it that you have an idle connection. It will return a new socket descriptor! This way, you have two sockets, the original one is still listening to your port, new in preparing for send (Send ()) and receives (RECV ()) data. This is this process!

The function is defined:

#include

INT Accept (int SockFD, Void * Addr, INT * AddRlen); SockFD is quite simple, and is the same socket descriptor as listen (). AddR is a pointer to the local data structure SOCKADDR_IN. This is the place where the information requiring access (you can measure the address in that port). Before its address is passed to Accept, AddRlen is a partial shaped variable set to sizeof (struct sockaddr_in). Accept will not give excess bytes to addR. If you put less, then it will change

The value of ADDRLEN is reflected.

Again, return -1 when an error is returned, and the global error variable errno is set.

Now is the code snippet you should be familiar.

#include

#include

#include

#define myport 3490 / * User Access Port * /

#define backlog 10 / * How much waiting for connection control * /

Main ()

{

INT SOCKFD, New_FD; / * Listen on Sock_fd, New Connection On New_FD * /

Struct SockAddr_in my_addr; / * address information * /

Struct SockAddr_in their_addr; / * connector's address information * /

INT SIN_SIZE;

Sockfd = Socket (AF_INET, SOCK_STREAM, 0); / * Error Check * /

MY_ADDR.SIN_FAMILY = AF_INET; / * HOST BYTE ORDER * /

My_addr.sin_port = htons (myport); / * short, network byte order * /

MY_ADDR.SIN_ADDR.S_ADDR = INADDR_ANY; / * Auto-Fill with my ip * /

Bzero (& (my_addr.sin_zero) ,; / * Zero the rest of the struct * /

/ * Don't forget your error Checking for these calls: * /

Bind (SockFD, Struct SockAddr *) & my_addr, sizeof (struct sockaddr);

Listen (Sockfd, Backlog);

SIN_SIZE = SIZEOF (struct sockaddr_in);

New_fd = accept (sockfd, & their_addr, & sin_size);

.

.

.

Note that you should use the new socket descriptor new_fd in the system calls Send () and RECV (). If you just want a connection, you can use Close () to close the original file descriptor SockFD to avoid more connections.

Send () and recv () function

These two functions are used in streaming sockets or data sets. If you like to use unconnected data sets, you should take a look at the section below Sendto () and Recvfrom ().

Send () is this:

Int Send (int Sockfd, Const Void * MSG, INT LEN, INT FLAGS);

SockFD is a socket descriptor you want to send data (or call socket () or accept () returns.) MSG is a pointer to the data you want to send. LEN is the length of the data. Setting the FLAGS to 0. (Detailed information, see Send () Man Page). Here is some possible examples:

Char * msg = "Beej Was Here!";

INT LEN, BYTES_SENT;

.

.

LEN = Strlen (MSG);

BYTES_SENT = Send (sockfd, msg, len, 0);

.

.

.

Send () Returns the number of bytes of data that actually sent - it may be less than the number you asked to send! Note, sometimes you tell it to send a bunch of data, but it can't handle success. It is just the data that it may send, and then you can send other data. Remember, if the data returned by Send () does not match, you should send other data. But there is also a good news here: if you want to send a small package (less than about 1K), it may handle the data once. Finally, it is, it returns -1 when it is wrong, and set Errno.

The RECV () function is very similar:

Int Recv (int Sockfd, Void * Buf, int LEN, UNSIGNED INT FLAGS);

Sockfd is the socket descriptor to read. BUF is the buffering of the information to read. Len is the maximum length of the buffer. Flags can be set to 0. (Please refer to the man page.) RECV () returns the number of bytes that actually read buffered data. Or return -1 when the error is returned, and Errno is set.

Very simple, isn't it? You can now send data and receive data on the stream socket. You are now a UNIX web programmer!

Sendto () and RECVFROM () functions

"This is very good", you said, "But you haven't said that there is no connection data settlement?" No problem, now we start this content.

Since the data settlement is not connected to the remote host, what information is needed before we send a package? Yes, it is the target address! Take a look at:

Int Sendto (int Sockfd, Const void * MSG, Int Len, unsigned int flags,

Const struct sockaddr * to, int toolen;

You have seen it, except for additional two information, the rest of the functions Send () is the same. TO is a pointer to the data structure Struct SockAddr, which contains the IP address and port information of the destination. Tolen can be simply set to sizeof (Struct SockAddr). Similar to the function send (), sendTo () returns the number of bytes actually sent (it may be less than the number of bytes you want to send!), Or return -1 at the time.

Similar features RECV () and RECVFROM (). The definition of Recvfrom () is like this:

Int Recvfrom (int SockFD, Void * Buf, Int Len, unsigned int flags, structure sockaddr * from, int * fromlen);

Another time, in addition to two additional parameters, this function and the RECV () are the same. From is a pointer to the local data structure Struct SockAddr, its content is the IP address of the source machine and port information. Fromlen is an INT type partial pointer, its initial value is SIZEOF (Struct SockAddr). After the function call returns, the future of the address saves the length of the address actually stored in the FROM. Recvfrom () Returns the received byte length or returns -1 after an error occurs.

Remember, if you connect a data settlement with Connect (), you can simply call Send () and RECV () to meet your requirements. At this time, it is still a data settlement, depending on UDP, the system socket interface will automatically add the target and source information for you.

Close () and shutdown () functions

You have been sending all day (Send ()) and reception (Recv ()) data, now you are ready to turn your socket descriptor. This is very simple, you can use the general Unix file descriptor's Close () function:

Close (SockFD);

It will prevent more data on more data on the socket. Any at an attempt to read a write set on the other end will return an error message.

If you want to control more about how to turn off the socket, you can use the function shutdown (). It allows you to close communication or two-way communications in a certain direction (just like close ()), you can use:

INT Shutdown (int Sockfd, int ~);

Sockfd is a socket file description you want to close. The value of how is one of the following:

0 - Do not allow acceptance

1 - Not allowed to send

2 - Do not allow sending and acceptance (with Close ())

ShutDown () returns 0 when successfully, returns -1 (setting Errno at the same time) if you use shutdown () in connectionless data sets, then let Send () and RECV () cannot be used ( Remember that you can use CONNECT in the data report setup).

GetPeerName () function

This function is too simple.

It is too simple, so I don't want to one list. But I still did this. The function getPeername () tells you who is on the other side on the connected stream socket. The function is like this:

#include

INT getPeername (int SockFD, Struct SockAddr * Addr, INT * AddRlen);

Sockfd is a descriptor for a streaming socket. AddR is a pointer to the structural struct sockaddr (or struct sockaddr_in), which holds information on the other side of the connection. Addrlen is an INT type pointer that is initialized to SIZEOF (Struct SockAddr). The function returns -1 when the error is wrong, set the corresponding Errno.

Once you get their address, you can print or get more information using inet_ntoa () or gethostbyaddr (). But you can't get its account. (If it runs a stupid daemon, it is possible, but its discussion has exceeded the scope of this article, please refer to RFC-1413 to get more information.)

Gethostname () function

Even more simple functions than getPeername () is gethostname (). It returns the host name of the machine running in your program. Then you can use gethostByName () to get the IP address of your machine.

The following is the definition:

#include

INT gethostname (char * hostname, size_t size); Parameter is very simple: Hostname is a character array pointer, which will be saved when the function returns

CPU name. SIZE is the byte length of the hostname array.

The function call returns 0 when the function calls, returns -1 when failing, and set errno.

Domain Name Service (DNS)

If you don't know the meaning of DNS, then I tell you that it represents the domain name service (Domain Name Service). Its main function is: You give it an address of a certain site that is easy to remember, it gives you an IP address (and then you can use bind (), connect (), sendto () or other functions). When one is entered:

$ TELNET Whitehouse.gov

Telnet knows it will connect (Connect ()) to "198.137.240.100".

But how is this work? You can call the function gethostbyname ():

#include

Struct Hostent * gethostByName (const char * name);

I understand that it returns a pointer to the Struct Hostent. This data structure is like this:

Struct hostent {

Char * h_name;

Char ** h_aliases;

INT H_ADDRTYPE;

Int h_gength;

Char ** h_addr_list;

}

#define h_addr h_addr_list [0]

Here is the details of this data structure:

Struct Hostent:

H_name - the official name of the address.

H_Aliases - a pointer for the preparatory name of the empty byte - address.

H_addrtype - address type; usually AF_INET.

H_LENGTH - The bit length of the address.

H_addr_list - zero byte - Host Network Address Pointer. Network byte order.

The first address in h_addr - h_addr_list.

GethostByName () returns a pointer to the structure Hostent, or a null pointer. (However, it is different from before, not set Errno, h_errno sets error message. Please see the following Herror ().)

But how to use it? Sometimes (we can discover from the computer manual), which is not enough to instill information to readers. This function is not as difficult as it looks like it looks.

Here is an example:

#include

#include

#include

#include

#include

#include

Int main (int Argc, char * argv [])

{

Struct hostent * h;

IF (argc! = 2) {/ * check command line * /

FPrintf (stderr, "usage: getip address / n");

Exit (1);

}

IF ((h = gethostByname) == null) {/ * acquisition address information * /

Herror ("gethostbyname");

Exit (1);

}

Printf ("Host Name:% S / N", H-> h_name);

Printf (IP Address:% S / N ", INET_NTOA (* (Struct In_ADDR *) H-> h_addr))); Return 0;

}

When using getHostByname (), you can't print the error message (because Errno is not used), you should call Herror ().

Quite simple, you just pass a string that saves the machine name (such as "Whitehouse.gov") gives gethostByName (), then get information from the returned data structure Struct Hostent.

The only thing that may make people don't understand the output IP address information. H-> h_addr is a char *, but INET_NTOA () is required for Struct in_addr. Therefore, I convert H-> h_addr into struct in_addr *, then get data.

Customer - server background knowledge

Here is a world of customers - servers. Everything on the network is in handling the conversation of the client's process and server process. Elite a telnet. When you log in to the host with the Telnet (Customer) through the 23rd port, a program running on the host (generally called telnetd, server) is activated. It handles this connection, display the login interface, and so on.

Figure 2: Relationship between clients and servers

Figure 2 illustrates information exchange between customers and servers.

Note that the customer-server can be used with Sock_Stream, Sock_DGRAM, or other (as long as they use the same). Some good customers - the server's example has Telnet / Telnetd, FTP / FTPD and BootP / Bootpd. Every time you use FTP, there is a ftpd at the far end for you.

Generally, there is only one server in the server, which uses fork () to handle the connection between multiple customers. The basic program is: the server waits for a connection, accepts the connection, and then a child process processes it. This is the next chapter of our examples.

Simple server

All of this server does send a string "Hello, World! / N" on streaming. If you want to test this program, you can run the program on a machine and then log in on another machine:

$ telnet remotehostname 3490

RemotehostName is the name of the machine running.

Server code:

#include

#include

#include

#include

#include

#include

#include

#include

#define myport 3490 / * Defines the user connection port * /

#define backlog 10 / * How much waiting for connection control * /

Main ()

{

INT SOCKFD, New_FD; / * Listen on Sock_fd, New Connection On New_FD

* /

Struct SockAddr_in my_addr; / * my address information * /

Struct SockAddr_in their_addr; / * connector's address information * /

INT SIN_SIZE;

IF ((SOCKFD = Socket (AF_INET, SOCK_STREAM, 0) == -1) {Perror ("socket"); exit (1);} my_addr.sin_family = AF_INET; / * HOST BYTE ORDER * / MY_ADDR.SIN_PORT = Htons (myport); / * short, network byte order * / my_addr.sin_addr.s_addr = INADDR_ADDR = INADDR_ADDR_ADDR = INADDR_ADDR_ADDR = INADDR_ADD * / * AUTO-FILL with My IP * / BZERO (& (My_addr.sin_zero) ,; / * Zero the rest of the structure * /

IF (Bind (Sockfd, (Struct SockAddr *) & my_addr, sizeof (struct sockaddr)) == -1) {Perror ("bind"); exit (1);} if (listen (sockfd, backlog) == -1 ) {PERROR ("listen"); exit (1);}

While (1) {/ * main accept () loop * / sin_size = sizeof (struct sockaddr_in); if ((new_fd = accept (sockfd, (strunt socketdr *)) == -1) {PERROR "accept"); Continue;} Printf ("Server: Got Connection from% S / N", / inet_ntoa (their_addr.sin_addr)); if (! fork ()) {/ * this is the child process * / if ( Send (new_fd, "hello, world! / n", 14, 0) == -1) PERROR ("send"); close (new_fd); exit (0);} close (new_fd); / * Parent doesn ' T NEED THIS * / WHILE (Waitpid (-1, null, wnohang> 0); / * Clean up child processes * /}} If you are very picky, you must not satisfy all my code is in a big main. () In the function. If you don't like it, you can divide more points. You can also use the programs in our next chapter to get the string sent by the server.

Simple customer program

This program is simpler than the server. All of this program is connected to the host specified in the command line through the 3490 port, and then get the string sent by the server.

Customer code:

#include

#include

#include

#include

#include

#include

#include

#include

#define port 3490 / * Client connection remote host port * /

#define maxDataSize 100 / * Maximum bytes you can receive each time * /

INT Main (int Argc, char * argv []) {

Int Sockfd, Numbytes;

Char buf [maxdatasize];

Struct hostent * he;

Struct SockAddr_in their_addr; / * connector's address information * /

IF (Argc! = 2) {fprintf (stderr, "usage: client hostname / n"); exit (1);} if ((he = gethostbyname) == null) {/ * get the Host Info * / Herror ("gethostByName"); exit (1);

IF ((SockFD = Socket (AF_INET, SOCK_STREAM, 0) == -1) {Perror ("socket"); exit (1);}

their_addr.sin_family = AF_INET; / * host byte order * / their_addr.sin_port = htons (PORT); / * short, network byte order * / their_addr.sin_addr = * ((struct in_addr *) he-> h_addr); bzero ( & (THEIR_ADDR.SIN_ZERO ,; / * ZERO The REST OF THE STRUCT * / IF (Connect (Sockfd, Struct SockAddr *) & their_addr, sizeof (struct sockaddr)) == -1) {PERROR ("Connect"); EXIT (1);} IF ((NumBytes = Recv (Sockfd, BUF, MaxDataSize, 0) == -1) {Perror ("RECV"); exit (1);} buf [numbytes] = '/ 0' PRINTF ("Received:% S", BUF); Close (Sockfd); Return 0;} Note that if you run the client before running the server, Connect () will return "Connection Refuse" information, which is very useful.

Packets Sockets I don't want to talk more, so I give the code Talker.c and Listener.c. Listener Wait for packets from port 4590 on the machine. Talker sends a packet to a certain machine that contains the contents of the user entered in the command line. Here is listener.c: #include #include #include #include #include #include #include #Define myport 4950 / * the port user maxbuffling to * / #define maxbufflen 100 main () {int sockfd; struct sockaddr_in my_addr; / * my address information * / struct sockaddr_in their_addr; / * connector's address information * / int addr_len, numbytes; char buf [MAXBUFLEN]; if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {perror ("Socket"); exit (1);} my_addr.sin_family = af_INET; / * HOST BYTE ORDER * / MY_ADDR.SIN_PORT = HTONS (MyPort); / * Short, network byte order * / my_addr.sin_addr.s_addr = incdr_any ; / * auto-fill with my ip * / bzero (& (my_addr.sin_zero) ,; / * Zero the rest of the structure * / if (bind (STRUCKFD, STRUCT SOCKADDR *) & my_addr, Sizeof (Struct SockAddr)) / == -1) {Perror ("bind"); exit (1);} addr_len = sizeof (struct sockaddr); if ((NUM) Bytes = Recvfrom (SockFD, BUF, MaxBuflen, 0, / (Struct Sockaddr *) & THEIR_ADDR, & Addr_len) == -1) {Perror ("Recvfrom"); exit (1);} Printf ("Got Packet from% s /N "PRINTF (" Packet IS); BUF [NumBytes] = '/ 0'; Printf ("Packet Contains /"% S / "/ n ", buf); close (sockfd); Note In our call socket (), we finally used SOCK_DGRAM. At the same time, there is no need to use listen () or accept ().

We are using unconnected datagram sleeves! The following is Talker.c: #include #include #include #include #include #include #include #include #define myport 4950 / * the port user (int Argc, char * argv [ion sockfd) Struct SockAddr_in their_addr; / * connector's address * he; int numBytes; if (Argc! = 3) {fprintf (stderr, "usage: talker hostname message / n"); exit (1);}

IF ((He = gethostByname) == null) {/ * get the host info * / human ("gethostbyname"); exit (1);}

IF ((SockFD = Socket (AF_INET, SOCK_DGRAM, 0) == -1) {Perror ("socket"); exit (1);}

their_addr.sin_family = AF_INET; / * host byte order * / their_addr.sin_port = htons (MYPORT); / * short, network byte order * / their_addr.sin_addr = * ((struct in_addr *) he-> h_addr); bzero ( & (THEIR_ADDR.SIN_ZERO ,; / * Zero the rest of the struct * / if ((NumBytes = Sendto (Sockfd, Argv [2], Strlen (Argv [2]), 0, / (Struct SockAddr *) & their_addr, SIZEOF (STRUCT SOCKADDR)) == -1) {Perror ("Sendto"); exit (1);} Printf ("SENT% D Bytes TO% S / N", NumBytes, INET_NTOA (THEIR_ADDR.SIN_ADDR)); Close (sockfd);} This is all. Run listner on a machine, then run Talker on another machine. Observe their communication! In addition to some of the data sockets I mentioned above In addition to the small details of the connection, I have to say some, when a speaker calls the connect () function and specifies the acceptor's address, from this point, the speaker can only go to connect ) The address specified by the function is sent and accepted. Therefore, you don't need to use Sendto () and Recvfrom (), you can use Send () and RECV () instead.

Blocking blocked, you may have heard it. "Blocking" is "Sleep" technology. You may notice the listener program running in front, it keeps running, waiting for the delivery of the packet. Actually running that it calls Recvfrom (), then there is no data, so the recvfrom () says "block" until the data is arriving. Many functions use blockage. Accept () blocking, all RECV * () functions block. The reason why they can do this is because they are allowed to do so. When you first call socket () to build a set of sets, the kernel is set to block it. If you don't want to block the block, you have to call the function fcntl (): #include #include .. Sockfd = socket (AF_INET, SOCK_STREAM, 0); FCNTL (SockFD, F_Setfl, O_nonblock);. By setting the socket as non-blocking, you can effectively "inquire" socket to get information. If you try to read information from a non-blocking socket and there is no data, it does not allow blocking - it will return -1 and set errno to Ewouldblock. But in general, this kind of inquiry is not a good idea. If you let your program query the data of the socket in a busy, you will waste a lot of CPU time. Better solution is to use the selection () to check if there is data to read.

SELECT () - Multi-Synchronous I / O Although this function is a bit strange, it is useful. Assuming this situation: You are a servers, you keep reading the information on the listening connection while talking on the connection from the connection. No problem, you may say, isn't it an accept () and two RECV ()? Is this easy, friend? If you block the accept ()? How can you accept RECV () data at the same time? "Use non-blocking sockets!" No! You don't want to exhaust all CPUs? So, how is it? Select () allows you to monitor multiple sockets at the same time. If you want to know, then it will tell you which socket is ready to read, which is ready to write, which socket has an exception. Gossip less, below is select (): # include #include #include int search (int Numfds, fd_set * readfds, fd_set * writefds, fd_set * ExceptFDS, Struct TimeVal * Timeout; This function monitors a series of file descriptors, especially READFDS, WRITEFDS, and ExcePTFDS. If you want to know if you can read the data from the standard input and socket descriptor SockFD, you only need to add file descriptor 0 and SockFD to the collection readfds. The parameter NUMFDS should be equal to the value of the highest file descriptor plus 1. In this example, you should set this value to SOCKFD 1. Because it must be larger than the standard input file descriptor (0). When the function select () is returned, the value of the readfds is modified to reflect which file descriptor you choose can read. You can test with the macro fd_isset () told below. Before we continue, let me talk about how to operate these collections. Each collection type is fd_set. There are some macros to operate on this type: fd_zero (fd_set * set) - Clear a file descriptor set fd_set (int FD, fd_set * set) - Add FD to Collect FD_CLR (int FD, FD_SET * SET) - From Collection Transfer to FD FD_ISSET (int FD, FD_SET * SET) - Test if the FD is finally in the collection, is a bit weird data structure Struct TimeVal. Sometimes you don't want to wait for others to send data. Maybe there is no matter what happened, you also want to print strings "Still Going ..." every 96 seconds. This data structure allows you to set a time, if time is, and select () has not found a ready-to-face descriptor, which will return to let you continue processing. The data structure Struct Timeval is this: struct timeval {int TV_sec; / * seconds * / int TV_usec; ​​/ * microseconds * /}; simply set TV_sec to you want to wait, set TV_USEC to you want to wait The number of seconds can be. Yes, it is microseconds instead of milliseconds. 1,000 microseconds or equal to 1 millisecond, 1,000 milliseconds is equal to 1 second. That is, 1 second is equal to 1,000,000 microseconds. Why is the symbol "usec"? The letter "U" is very like a Greek letter MU, and MU means "micro" meaning.

Of course, Timeout may be the remaining time when the function returns, because it relies on your UNIX operating system. what! We now have a micro-second timer! Don't calculate, the standard UNIX system time film is 100 milliseconds, so no matter how you set your data structure Struct Timeval, you have to wait for a long time. There are also some interesting things: if you set the data in the data structure Struct TimeVal is 0, select () will immediately time out, so you can effectively poll all the file descriptors in the collection. If you assign the parameter Timeout as null, you will never have timeout, you will wait until the first file descriptor is ready. Finally, if you are not very concerned about waiting for how long, then assign it as null. The following code demonstrates waiting for 2.5 seconds on standard input: #include #include #include #define stdin 0 / * file descriptor for Standard Input * / main () {struct timevalval TV; fd_set readfds; TV.tv_sec = 2; TV.tv_usec = 500000; fd_zero (& readfds); fd_set (stdin, & readfds); / * don't Care About WriteFDS and ExcePTFDS: * / SELECT (Stdin 1, & READFDS, NULL, NULL, & TV); IF (fd_isset (stdin, & readfds)) Printf ("a key was press! / N"); Else Printf ("TIMED OUT./N");} You are on a Line Buffered terminal, then the key you knock should be RETURN, otherwise it will time out anyway. Now, you may return to think that this is the way you wait for data on the data settlement - you are right: it may be. Some UNIX systems can be in this way, while others can't. You may have to look at the Man Page of this system before trying. The last thing about SELECT (): If you have a socket that is listening (Listen () () ()), you can see if there is a new one by adding the file descriptor of the socket to the ReadFDS collection connection. This is what I have to say about the function select ().

Reference Bibliography: Internetworking with TCP / IP, Volumes I-III by Douglas E. COMER AND DAVID L. Stevens. Published by Prentice Hall. Second Edition ISBNS: 0-13-468505-9, 0-13-472242-6, 0 -13-474222-2. There is a third edition of this set Which cover IPv6 and ip overdm. Using c on the UNIX System by David A. Curry. Published by O'Reilly & Associates, Inc. ISBN 0-937175- 23-4. TCP / IP NetWork Administration by Craig Hunt. Published by O'Reilly &

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

New Post(0)