[The evolution of TCP server] Writing the first TCP server: achieving one-to-one connection communication

I. Introduction

Teach you step by step how to write a TCP server program from scratch, and experience how you can build a building with only one brick at the beginning .

In order to avoid being too long and boring the reader, [TCP server development] is implemented in stages and optimized and upgraded step by step.
Insert image description here

2. APIs that need to be used

2.1. socket() function

Function prototype:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);

This function creates a socket file descriptor with protocol family, protocol type, and protocol number. If the function call is successful, it will return a file descriptor identifying the socket. If it fails, it will return -1 and set errno.

Domain parameter value meaning:

name meaning
PF_UNIX,PF_LOCAL local communication
AF_INET,PF_INET IPv4 protocol
PF_INET6 IPv6 protocol
PF_NETLINK kernel user interface device
PF_PACKET Low-level package access

Type parameter value meaning:

name meaning
SOCK_STREAM TCP connection provides a serialized, reliable, bidirectional connection byte stream. Supports out-of-band data transfer
SOCK_DGRAM UDP connection
SOCK_SEQPACKET Serialization package provides a serialized, reliable, two-way data transmission channel with constant data length. Every time the read system call is called, all data needs to be read out.
:SOCK_PACKET Special type
SOCK_RDM Provide reliable data packets, but do not guarantee that the data is in order
SOCK_RAW Provides raw network protocol access

Meaning of the protocol parameter:
Usually there is only one specific type in a protocol, so the protocol parameter can only be set to 0; if the protocol has multiple specific types, you need to set this parameter to select a specific type.

2.2. bind() function

Function prototype:

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

Parameter Description:

  • The first parameter sockfd is the file descriptor created with the socket() function.
  • The second parameter my_addr is a pointer to a structure of sockaddr parameters. sockaddr contains address, port and IP address information.
  • The third parameter addrlen is the length of the my_addr structure, which can be set to sizeof(struct sockaddr).

When the return value of the bind() function is 0, it means the binding is successful, and -1 means the binding failed and errno is set.

2.3. listen() function

Function prototype:

#include<sys/socket.h>
int listen(int sockfd, int backlog);

Parameter Description:

  • The first parameter sockfd is the file descriptor created with the socket() function.
  • The second parameter backlog specifies the maximum number of connections that the kernel should queue for the corresponding socket.

2.4. accept() function

Function prototype:

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

Parameter Description:

  • sockefd: Socket descriptor that listens for connections after listen().
  • addr: (optional) pointer. Pointer to a buffer that receives the addresses of connected entities known to the communication layer. The actual format of the Addr parameter is determined by the address family generated when the socket was created.
  • addrlen: (optional) pointer. Input parameter, used together with addr, points to an integer holding the length of addr address.

2.5. recv() function

Function prototype:

#include<sys/types.h>
#include<sys/socket.h>
int recv( int fd, char *buf, int len, int flags);

Parameter Description:

  • The first parameter specifies the receiving socket descriptor;
  • The second parameter specifies a buffer, which is used to store the data received by the recv function;
  • The third parameter specifies the length of buf;
  • The fourth parameter is generally set to 0.

return value:

  • Returns a number greater than 0, indicating the introduced data size.
  • Returns 0, indicating that the connection is disconnected.
  • Returns -1, indicating an error in accepting data.

2.6. send() function

Function prototype:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

Parameter Description:

  • sockfd: Send data to the socket
  • buf: the first address of the data to be sent
  • len: bytes of data to be sent
  • int flags: When set to MSG_DONTWAITMSG, it means non-blocking. When set to 0, the function is the same as write.

Return value: The actual number of bytes sent is returned on success, -1 is returned on failure and errno is set.

2.7. strerror() function

strerror()The function returns a pointer to a string describing the error code passed in parameter errnum, possibly using the LC_MESSAGES part of the current locale to select the appropriate language. (For example, if errnum is EINVAL, the returned description will be "Invalid Parameter".) This string cannot be modified by the application, but can be modified by subsequent calls to strerror()or strerror_l(). perror()This string will not be modified by any other library functions, including

Function prototype:

#include <string.h>

char *strerror(int errnum);

int strerror_r(int errnum, char *buf, size_t buflen);
/* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
/* GNU-specific */
char *strerror_l(int errnum, locale_t locale);

3. Implementation steps

One-to-one server design:
Insert image description here

(1) Create socket.

int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd==-1){
    
    
    printf("errno = %d, %s\n",errno,strerror(errno));
    return SOCKET_CREATE_FAILED;
}

(2) Binding address.

struct sockaddr_in server;
memset(&server,0,sizeof(server));

server.sin_family=AF_INET;
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(LISTEN_PORT);

if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
    
    
    printf("errno = %d, %s\n",errno,strerror(errno));
    close(listenfd);
    return SOCKET_BIND_FAILED;
}

(3) Set up monitoring.

if(-1==listen(listenfd,BLOCK_SIZE)){
    
    
   printf("errno = %d, %s\n",errno,strerror(errno));
   close(listenfd);
   return SOCKET_LISTEN_FAILED;
}

(4) Receive connection.

struct sockaddr_in client;
memset(&client,0,sizeof(client));
socklen_t len=sizeof(client);
    
int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
if(clientfd==-1){
    
    
    printf("errno = %d, %s\n",errno,strerror(errno));
    close(listenfd);
    return SOCKET_ACCEPT_FAILED;
}

(5) Receive data.

char buf[BUFFER_LENGTH]={
    
    0};
ret=recv(clientfd,buf,BUFFER_LENGTH,0);
if(ret==0) {
    
    
     printf("connection dropped\n");

}
printf("recv --> %s\n",buf);

(6) Send data.

if(-1==send(clientfd,buf,ret,0))
{
    
    
    printf("errno = %d, %s\n",errno,strerror(errno));
}

(7) Close the file descriptor.

close(clientfd);
close(listenfd);

4. Complete code

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#include <errno.h>
#include <string.h>
#include <unistd.h>


#define LISTEN_PORT     8888
#define BLOCK_SIZE      10
#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    
    
    SOCKET_CREATE_FAILED=-1,
    SOCKET_BIND_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4
};



int main(int argc,char **argv)
{
    
    
    // 1.
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd==-1){
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;
    }

    // 2.
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));

    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port=htons(LISTEN_PORT);

    if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_BIND_FAILED;
    }

    // 3.
    if(-1==listen(listenfd,BLOCK_SIZE)){
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_LISTEN_FAILED;
    }

    // 4.
    struct sockaddr_in client;
    memset(&client,0,sizeof(client));
    socklen_t len=sizeof(client);
    
    int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
    if(clientfd==-1){
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_ACCEPT_FAILED;
    }

    printf("client fd = %d\n",clientfd);
    int ret=1;
    while(ret>0){
    
    
        // 5.
        char buf[BUFFER_LENGTH]={
    
    0};
        ret=recv(clientfd,buf,BUFFER_LENGTH,0);
        if(ret==0) {
    
    
            printf("connection dropped\n");
            break;

        }
        
        printf("recv --> %s\n",buf);
        if(-1==send(clientfd,buf,ret,0))
        {
    
    
            printf("errno = %d, %s\n",errno,strerror(errno));
        }
        
    }
    close(clientfd);
    close(listenfd);

    return 0;
}

Compile command:

gcc -o server server.c

5. TCP client

5.1. Implement a TCP client yourself

Implement the code for a TCP client to connect to the TCP server yourself:

#include <stdio.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <string.h>

#include <unistd.h>
#include <stdlib.h>

#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    
    
    SOCKET_CREATE_FAILED=-1,
    SOCKET_CONN_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4
};

int main(int argc,char** argv)
{
    
    
    if(argc<3)
    {
    
    
        printf("Please enter the server IP and port.");
        return 0;
    }
    printf("connect to %s, port=%s\n",argv[1],argv[2]);

    int connfd=socket(AF_INET,SOCK_STREAM,0);
    if(connfd==-1)
    {
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;

    }
    struct sockaddr_in serv;
    serv.sin_family=AF_INET;
    serv.sin_addr.s_addr=inet_addr(argv[1]);
    serv.sin_port=htons(atoi(argv[2]));
    socklen_t len=sizeof(serv);
    int rwfd=connect(connfd,(struct sockaddr*)&serv,len);
    if(rwfd==-1)
    {
    
    
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(rwfd);
        return SOCKET_CONN_FAILED;
    }
    int ret=1;
    while(ret>0)
    {
    
    
        char buf[BUFFER_LENGTH]={
    
    0};
        printf("Please enter the string to send:\n");
        scanf("%s",buf);
        send(connfd,buf,strlen(buf),0);

        memset(buf,0,BUFFER_LENGTH);
        printf("recv:\n");
        ret=recv(connfd,buf,BUFFER_LENGTH,0);
        printf("%s\n",buf);
        
    }
    close(rwfd);
    return 0;
}

Compile:

gcc -o client client.c

5.2. You can use the NetAssist network assistant tool under Windows

Insert image description here

Download address: http://old.tpyboard.com/downloads/NetAssist.exe

summary

At this point, we have achieved a one-to-one server connection. The TCP server code at this stage can only receive one client access. If the client is disconnected, it will exit directly. The focus is on mastering the basic process of developing a TCP server. The next chapter will introduce upgrading on this basis to accept simultaneous access from multiple clients.
Insert image description here

Guess you like

Origin blog.csdn.net/Long_xu/article/details/135118661