Socket and Multithreaded Practice

Posted by furma on Sun, 03 Oct 2021 21:40:38 +0200

Review of the previous section:
We send data from the application layer to senders and receivers in different buildings. The transport layer has two protocols: TCP / UDP
TCP / UDP can be seen as a parallel universe, without interruption. Receiver and sender can use TCP / UDP
Each port of the server is isolated from each other.
Understand TCP as Express Company A, UDP as Express Company B
Sockets are understood as couriers.
Layered:
Layer 1: Yes 2 16 2^{16} 216 Port, the courier company gives each user a port, you need to apply for one when you want to use it, but at the same time you need to hire a courier socket. Different courier companies do not have ports that conflict. If you want to use both A and B, you need to hire two couriers in two different companies.
Users on both sides need a courier for each user to communicate. In the process of communication, the courier companies on both sides need to be unified. When the program gets the data, the socket receives the information to the courier company, and then the courier company at a will transport the courier to the courier company at b in an unknown way.
It looks like the port is 1 v1, but when accept, a new sockfd is generated.
Start with the socket function,
Then bind the socket to the port (x)
The original sockfd was active, using listen became passive
The original sockfd can accept any accept and shake hands three times to return a newfd, which is responsible for the connection just established. This newfd is also associated with the port (x). Here's the information. How can we tell?
So, we need to reuse, the new file knows who the port is and who the people who connect with it are, so sockfd is a quaternion (from ip, from port, to ip, to port), information to the port, then the kernel knows how many files this port has, and then gives that information to which file (transparency, let's not go into details first).
Unused file identifiers need to be closed. A total of 1024 files can be allocated. If the redundant file descriptors are not closed, subsequent connections will fail and the socket returned by accept will be closed.

When bind, ports have certain requirements. When setting ports, well-known ports less than 1024 will have insufficient privileges. When you really want to use this port, you can use sudo to privilege.

Acept handshakes three times, receives syn packets, and returns syn + ack when SYN is received...
However, there may be errors when shaking hands. If there are system errors or insufficient file descriptors at this time, there may be no more than three shakes, which will lead to failure.
When successful, a new file descriptor is created identifying each other's ip, port, and information.
You want to interact, but when a computer process does not have any branches, it only has one time to communicate with one person. If there are many people who want to interact with you for a long time in the future, but because they have been communicating with that person, others will be blocked when they shake hands with you three times.
A subprocess is created here.
Here the child process does not need server_listen, so it closes the sever_listen file, and the parent process closes the new file descriptor. (The parent process closes the file descriptor that the parent process does not need.)In a system, when two processes open a file, the number of connections to the file in the system increases. When closed, the number of connections to the system decreases by only one, not closing the file completely. (This will not affect the business, but will increase the robustness of the business.)

When do you receive the end message? When recv returns 0, 0 closes the connection, and less than 0 is an error.

recv is like a courier who goes to carry things and gives him a buffer. He is asked to carry things with such a large buffer. When the courier goes, he finds out how much and how much data he does not have.
This causes a problem: unpacking
When something with two buffers is sent, recv receives part of it and we need to unpack it later.
When something is sent that is very small than the buffer, recv may receive something with matchbox, so we need to do the sticking problem.
So we need to be aware. The package you send doesn't necessarily mean you receive it.
If recv is not collected at one time, it will be manually called the next time.

We can't just accept one packet at a time. The underlying IP network sends packets on different routes to their destinations, so it's possible that a packet will arrive late on the way, so subsequent packages will wait for the package to arrive.

The number in the backlog in the bind is the number of connections being made, not the number of connections already made.

There is a problem here. When multiple processes are involved, the parent process creates a socket, when fork is involved, the child process creates a socket, and the child process inherits the socket. So who is this information to when the message comes?
Who gets it first, but that's not what we want, so in multiple processes, the first sentence of a child process usually closes the sockfd file created in the parent process (without affecting the parent process)

Signal:
Signals vary with the system

#include <sys/types.h>
#include <signal.h>

int kill(pid_t, int sig);
/*
	* send signal to a process
	* Signal a process
	* The signal is similar to a ctrl + c or clock.
	* If sig = 0, no signal will be given, but the process will also be checked, no privileges, or other errors will occur
	* kill -l
	* 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 	* 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
	* 11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
	* 16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
	* 21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
	* 26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
	* 31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
	* 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
	* 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
	* 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
	* 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
	* 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
	* 63) SIGRTMAX-1  64) SIGRTMAX
	* Mouse Catcher Principle: A signal is coming, and later signals may be missed
	*
	* 
	* signal
	* typedef void (*sighanler_t)(int);
	* sighandler_t signal(int signum, sighandler_t handler);
	* Signals vary from UNIX version to version, to avoid these things, use sigaction instead
	* signal Set signum for processing, typically
	* SIG_IGN Ignore signal
	* SIG_DFL : signal_default Default signal, if this is set by the processing, then the default operation of the signal will be associated with this
	* Or a signal function defined by the programmer
	* This signal is assigned to the process,
	* If this function is set, either the function is set to SIG_DFL first, or the signal is colored, and then the signal processing function is called. If the signal is blocked before the signal processing, then the next signal may not be blocked and go directly to it. That is, the next signal cannot be processed, so it is also called the processing of the rodent catcher principle.
	* Later you will learn solutions
*/
/*************************************************************************
        > File Name: 1.server.c
        > Author: Monster
        > Mail: 2788490159@qq.com
        > Created Time: Thu 03 Jun 2021 02:01:53 AM CST
 ************************************************************************/

#include "head.h"

int main(int argc, char **argv) {
    int server_listen, sockfd, port;//server_listen: Server listens, discarding the ability to actively contact
    if (argc != 2) {
        fprintf(stderr, "Usage : %s port!\n", argv[0]);
        exit(1);
    }
    port = atoi(argv[1]);
    if ((server_listen = socket_create(port)) < 0) {// Output information if internal error occurs in the internal request
        perror("socket_create");
        exit(1);
    }
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    while (1) {//Dead cycle, waiting indefinitely

        if ((sockfd = accept(server_listen, (struct sockaddr *)&client, &len)) < 0) {//Just wait for syn packages, return ack with syn packages, shake hands three times...
            perror("accpept");
            exit(1);//Possible errors during three handshakes
        }
        printf("<%s> is online\n", inet_ntoa(client.sin_addr));
        pid_t pid;
        if ((pid = fork()) < 0) {//If no child process is used, the parent process will block the data transfer and cannot interact with other processes
            exit(1);
        }
        if (pid == 0) {
            close(server_listen);//Subprocess shuts down server_listen
            while (1) {
                char buff[512] = {0};
                char tobuff[512] = {0};
                size_t ret = recv(sockfd, buff, sizeof(buff), 0);// recv will receive everything every time, but there will be a problem with unpacking. If there is too much information, it cannot be received at one time, or too little information, it is easy to glue with other information, so there will be sticky and unpacking problems.
                // If recv is not finished at one time, the user needs to recv again actively, the system will not recv automatically
                if (ret <= 0) {
                    printf("<%s> is offline\n", inet_ntoa(client.sin_addr));
                    close(sockfd);
                    exit(1);
                }
                printf("<%s> says : %s\n", inet_ntoa(client.sin_addr), buff);
                if (strcmp(buff, "bye~886") == 0) {
                    sprintf(tobuff, "We will say bye and over, bye~");
                    send(sockfd, tobuff, strlen(tobuff), 0);
                    close(sockfd);
                    exit(1);
                } else {
                    sprintf(tobuff, "I`ve recved the mes : %s", buff);
                    send(sockfd, tobuff, strlen(tobuff), 0);
                }
            }
            return 0;
        } else {
            close(sockfd); // Associate parent and child processes. Shutdown here reduces the number of associations sockfd has in the process. When the number of associations is 0, the file is deleted
        }
    }
}
/*************************************************************************
        > File Name: 1.client.c
        > Author: Monster
        > Mail: 2788490159@qq.com
        > Created Time: Thu 03 Jun 2021 02:08:20 AM CST
 ************************************************************************/

#include "head.h"

int sockfd;

void logout(int signum) {
    close(sockfd);
    printf("\nByeBye!\n");
    exit(0);
}

int main(int argc,char **argv) {
    int port;
    char buff[512] = {0}, ip[20] = {0};
    char tobuff[512] = {0};
    if (argc != 3) {
        fprintf(stderr, "Usage : %s ip port \n",argv[0]);
        exit(1);
    }

    strcpy(ip, argv[1]);
    port = atoi(argv[2]);
    //signal(SIGINT, SIG_IGN);Setting this will ignore the exit signal
    signal(SIGINT, logout);// Set Signal
    if ((sockfd = socket_connect(ip, port)) < 0) {
        perror("socket_connect");
        exit(1);
    }

    while (1) {
        scanf("%[^\n]s", buff);
        getchar();
        if (!strlen(buff)) continue;
        send(sockfd, buff, strlen(buff), 0);
        recv(sockfd, tobuff, sizeof(tobuff), 0);
        if (strlen(tobuff)) printf("recv : %s\n", tobuff);
        bzero(tobuff, sizeof(tobuff));
        bzero(buff, sizeof(buff));
    }

    return 0;
}

Topics: udp TCP/IP