Socket programming, socket

Source: Internet
Author: User
Tags unix domain socket htons

Socket programming, socket

I. Data Types and related functions of socket addresses

Socket API is an abstract network programming interface that is applicable to various underlying network protocols, such as IPv4, IPv6, and UNIX Domain Socket. However, the address formats of various network protocols are different, as shown in:

The IPv4 and IPv6 address formats are defined in netinet/in. in h, the IPv4 address is represented by the sockaddr_in structure, including the 16-bit port number and 32-bit IP address. The IPv6 address is represented by the sockaddr_in6 structure, including the 16-bit port number, 128-bit IP address, and some control fields. The address format of UNIX Domain Socket is defined in sys/un. h and is represented by the sockaddr_un struct. The prefixes of various socket address struct are the same. The first 16 bits indicate the length of the entire struct (not all UNIX implementations have a length address, but Linux does not ), the last 16 bits indicate the address type. The address types of IPv4, IPv6, and UNIX Domain Socket are defined as the constant AF_INET, AF_INET6, and AF_UNIX respectively. In this way, as long as you get the first address of a sockaddr struct, you do not need to know which type of sockaddr struct is the prop body, you can determine the content in the struct according to the address type field. Therefore, the socket API can accept various types of sockaddr struct pointers as parameters, such as bind, accept, connect, and other functions, the parameters of these functions should be designed to be void * type to accept various types of pointers. However, the implementation of sock APIS is earlier than that of ansi c, and there is no void * type at that time, therefore, the parameters of these functions are represented by the struct sockaddr * type. before passing the parameters, force type conversion. For example:

struct sockaddr_in servaddr;/* initialize servaddr */bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));

Struct in_addr sin_addr, a member in sockaddr_in, indicates a 32-bit IP address. However, we usually use a dot-decimal string to represent the IP address. The following functions can be converted between string representation and in_addr representation.

Function for converting string to in_addr:

#include <arpa/inet.h>int inet_aton(const char *strptr, struct in_addr * addrptr);in_addr_t inet_addr(const char *strptr);int inet_pton(int family, const char *strptr, void *addrptr);

Function for converting in_addr to string:

char *inet_ntoa(struct in_addr inaddr);const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

Inet_ton and inet_ntop can not only convert the in_addr of IPv4, but also the in6_addr of IPv6. Therefore, the function interface is void * addrptr.

2. TCP-based Network Program

1. TCP communication process

Connection establishment process:

After the server calls socket (), bind (), and listen () to complete initialization, it calls accept () to block the wait and is in the listening port State. After the client calls socket () for initialization, call connect () to issue SYN segments and block wait for the server to respond, the server responds to a SYN-ACK segment, the client returns from connect () Upon receipt, and responds to an ACK, the server returns the result from accept () After receiving the message.

Data transmission process:

After a connection is established, the TCP protocol provides full-duplex communication services. However, generally, the client/server program initiates a request actively, and the server passively processes the request in a one-Answer manner. Therefore, the server immediately calls read () after returning from accept (). The read socket is like a read pipeline. If no data arrives, the wait will be blocked. This is when the client calls write () the server sends a request to the server. After receiving the request, the server returns it from read () and processes the request from the client. During this period, the client calls read () to block waiting for the server's response. The server calls write () send the processing result to the client and call read again to block the next request. After receiving the result, the client returns the result from read () and sends the next request.

Closing the connection:

If the client has no more requests, call close () to close the connection. Just like the pipeline closed by the write end, the server returns 0 for read, in this way, the server knows that the client has closed the connection and also calls close () to close the connection. Note: After either party calls close (), both transmission directions of the connection are closed and data cannot be sent. If one party calls shutdown (), the connection is in the semi-closed state and can still receive data from the other party.

2. TCP network program

Server. c reads characters from the client, converts each character to uppercase, and returns it to the client.

/* server.c */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#define MAXLINE 80#define SERV_PORT 8000int main(void){        struct sockaddr_in servaddr, cliaddr;        socklen_t cliaddr_len;        int listenfd, connfd;        char buf[MAXLINE];        char str[INET_ADDRSTRLEN];        int i, n;        listenfd = socket(AF_INET, SOCK_STREAM, 0);        bzero(&servaddr, sizeof(servaddr));        servaddr.sin_family = AF_INET;        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);        servaddr.sin_port = htons(SERV_PORT);            bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));        listen(listenfd, 20);        printf("Accepting connections ...\n");        while (1) {                cliaddr_len = sizeof(cliaddr);                connfd = accept(listenfd,                                 (struct sockaddr *)&cliaddr, &cliaddr_len);                          n = read(connfd, buf, MAXLINE);                printf("received from %s at PORT %d\n",                       inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),                       ntohs(cliaddr.sin_port));                    for (i = 0; i < n; i++)                        buf[i] = toupper(buf[i]);                write(connfd, buf, n);                close(connfd);        }}

The socket APIs used in the program are all in sys/socket. h.

int socket(int family, int  type, int protocol);

Socket () opens a network communication port. If it succeeds, a file descriptor is returned like open, applications can use read/write to send and receive data over the network just like reading and writing files. If an error occurs in a socket call,-1 is returned. for IPv4, the family parameter is set to AF_INET. For TCP, the type parameter is set to SOCK_STREAM, which indicates the stream-oriented transmission protocol. If the UDP protocol is used, the type parameter is specified as SOCK_DGRAM, which indicates the datagram-oriented transmission protocol. Set the protocol parameter to 0.

int bind(int sockfd, const struct sockaddr &myaddr, socklen_t addrlen);

The network address and port number listened on by the server program are usually fixed. After the client program learns the address and port number of the server program, it can initiate a connection to the server, therefore, the server needs to call bind to bind a fixed network address and port number. If bind () is successful, 0 is returned. If it fails,-1 is returned.

Bind () is used to bind sockfd and myaddr, so that the file descriptor used for network communication listens to the address and port number described by myaddr. Struct sockaddr * is a common pointer type. The myaddr parameter can actually receive sockaddr structs of multiple protocols, and their lengths are different. Therefore, the third parameter addrlen is required to specify the length of the struct. In our program, the myaddr parameter is initialized as follows:

bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);

First, clear the entire struct, set the address type to AF_INET, and the network address to INADDR_ANY. This macro indicates any local IP address, because the server may have multiple NICs, each Nic may also be bound to multiple IP addresses, so that you can listen on all IP addresses until we establish a connection with a client to determine which IP address to use and the port number is SERV_PORT, it is defined as 8000.

int listen(int sockfd, int backlog);

A typical server program can serve multiple clients at the same time. When a client initiates a connection, the accept () called by the server returns and accepts the connection, if a large number of clients initiate a connection and the server is too late to process it, the clients that have not yet accept () are in the connection waiting state. If more connection requests are received, ignore them. If listen () is successful, 0 is returned. If yes,-1 is returned.

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

After the three-party handshake is complete, the server calls accept () to accept the connection. If the server still does not have a client connection request when it calls accept (), it will block waiting until a client connection comes up. Cliaddr is an outgoing parameter. The client address and port number are returned by accept. The addrlen parameter is a value-result argument parameter. It refers to the length of the cliaddr buffer provided by the caller to avoid Buffer Overflow, the actual length of the client address struct is transmitted (it is possible that the buffer provided by the caller is not fully occupied ). If the cliaddr parameter is NULL, the client address is not concerned.

Our server program structure is as follows:

while (1) {    cliaddr_len = sizeof(cliaddr);    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);    n = read(connfd, buf, MAXLINE);    ...    close(connfd);}

The entire process is a while endless loop. Each loop processes a client connection. Since cliaddr_len is an outgoing parameter, the initial value should be re-assigned before each call to accept. The listenfd parameter of accept () is the previous listener file descriptor, while the return value of accept () is another file descriptor connfd, which then communicates with the client and through this connfd, close the connfd and disconnect without closing the listenfd. Return to the beginning of the loop and the listenfd is still used as the accept parameter. If accept () is successful, a file descriptor is returned. If an error occurs,-1 is returned.

Client. c obtains a string from the command line parameters and sends it to the server. Then, it accepts the string returned by the server and displays it:

/* client.c */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#define MAXLINE 80#define SERV_PORT 8000int main(int argc, char *argv[]){        struct sockaddr_in servaddr;        char buf[MAXLINE];        int sockfd, n;        char *str;            if (argc != 2) {                fputs("usage: ./client message\n", stderr);                exit(1);        }        str = argv[1];            sockfd = socket(AF_INET, SOCK_STREAM, 0);        bzero(&servaddr, sizeof(servaddr));        servaddr.sin_family = AF_INET;        inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);        servaddr.sin_port = htons(SERV_PORT);            connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));        write(sockfd, str, strlen(str));        n = read(sockfd, buf, MAXLINE);        printf("Response from server:\n");        write(STDOUT_FILENO, buf, n);        close(sockfd);        return 0;}

Because the client does not need a fixed port, it does not need to call bind (). The client port number is automatically allocated by the kernel. (The server does not have to call bind (), but if you do not call bind (), the kernel will automatically allocate a listening port to the server, each time you start the server, the port number is different. It is troublesome for the client to connect to the server)

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

The client needs to call connect () to connect to the server. The conenct and bind parameters are in the same format. The difference is that the bind parameters are their own addresses, while the connect parameters are the other party's addresses. If connect () succeeds, 0 is returned. If an error occurs,-1 is returned.

Then compile and run the server before running the client.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.