Chapter 1 of "TCP IP Network Programming"

        2023.6.28 Officially started learning network programming. Notes for each chapter and section will be recorded in the blog for review.


Chapter 1

1.1 Understanding Network Programming and Sockets

        Network programming is also called socket programming. The so-called network programming is to write programs to allow two networked computers to exchange data with each other. Why is it called socket programming? We usually get power from the power grid by inserting the plug into the socket. In the same way, in order to transmit data with a remote computer, we need to connect to the Internet, and the "socket" in programming is a tool used to connect to the network.

        The socket created on the server side is also called a server-side socket or a listening socket. The socket creation process for requesting a connection is divided into four steps:

  1. Call the socket function to create a socket
  2. Call the bind function to assign IP address and port number
  3. Call the listen function to switch to the request-acceptable state
  4. Call the accept function to accept the connection request

        The creation process of a client socket requesting a connection is as follows:

  1. Call the socket function to create a socket
  2. Call the connect function to send a connection request to the server

        It is worth noting that after creating the socket, it will not be immediately distinguished as server or client. If the bind, listen and other functions are called next, it becomes a server-side socket; if the connect function is called, it is called a client socket.

        Next, compile and execute the above two examples in the Linux environment: hello_server.c file and hello_client.c file.

        Compile the client and server programs separately:    

gcc hello_server.c -o hserver
gcc hello_client.c -o hclient

        -o in this command is an optional parameter used to specify the executable file name. Therefore, the executable files hserver and hclient will be generated after compilation.

            run:

./hserver 9190
./hclient 127.0.0.1 9190

        When running, first start the service on port 9190, and then heserver will wait for the client to respond. When the client listens to port 9190 at the local IP address of 127.0.0.1, the client will receive a response from the server. , output `Hello World!`.

        ps: The 127.0.0.1 entered during execution is the IP address of the local computer. This connection method will be used if both the server and the client are running on the same computer. However, if the server and client are running on different computers, the IP address of the computer where the server is located should be used.


1.2 Linux-based file operations

                                                  Low-level access and file descriptors

        In Linux, socket is also a type of file, so file I/O related functions can be used during network data transmission. Windows needs to distinguish between sockets and files, so special data transfer related functions need to be called in Windows.

        Whenever a file or socket is generated, the operating system will return the integer assigned to them, (i.e. a file descriptor). This integer will become a good communication channel between the programmer and the operating system. In fact, the file descriptor is for A number conveniently assigned to a file or socket created by the system. File descriptors are also called file handles, "handle" is the term in Windows, and Linux platforms use "descriptor".

                                                        open a file

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int flag);
/*
成功时返回文件描述符,失败时返回-1
path : 文件名的字符串地址
flag : 文件打开模式信息
*/

                                                         close file

#include <unistd.h>
int close(int fd);
/*
成功时返回 0 ,失败时返回 -1
fd : 需要关闭的文件或套接字的文件描述符
*/

        If this function is called with the file descriptor argument passed, the response file is closed (terminated). Another thing to note is that this function can not only close files, but also sockets. It once again proves the characteristic of "Linux operating system does not distinguish between files and sockets". 

                                                    Write data to file

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
/*
成功时返回写入的字节数 ,失败时返回 -1
fd : 显示数据传输对象的文件描述符
buf : 保存要传输数据的缓冲值地址
nbytes : 要传输数据的字节数
*/

        In the definition of this function, size_t is an unsigned int type declared via typedef. For ssize_t, the extra s in front of ssize_t represents signed, that is, ssize_t is a signed int type declared through typedef.

Create new file and save data:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void error_handling(char *message);

int main()
{
    int fd;
    char buf[] = "Let's go!\n";
    // O_CREAT | O_WRONLY | O_TRUNC 是文件打开模式,将创建新文件,并且只能写。如存在 data.txt 文件,则清空文件中的全部数据。
    fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);
    if (fd == -1)
        error_handling("open() error!");
    printf("file descriptor: %d \n", fd);
    // 向对应 fd 中保存的文件描述符的文件传输 buf 中保存的数据。
    if (write(fd, buf, sizeof(buf)) == -1)
        error_handling("write() error!");
    close(fd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

         After running, a data.txtfile will be generated, which containsLet's go!

                                                        Read data from file

write()Corresponding         to the previous function, read()函数used to input (receive) data:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
/*
成功时返回接收的字节数(但遇到文件结尾则返回 0),失败时返回 -1
fd : 显示数据接收对象的文件描述符
buf : 要保存接收的数据的缓冲地址值。
nbytes : 要接收数据的最大字节数
*/

        The following code reads the data saved in data.txt through the read() function:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 100
void error_handling(char *message);

int main()
{
    int fd;
    char buf[BUF_SIZE];

    fd = open("data.txt", O_RDONLY);
    if (fd == -1)
        error_handling("open() error!");
    printf("file descriptor: %d \n", fd);

    if (read(fd, buf, sizeof(buf)) == -1)
        error_handling("read() error!");
    printf("file data: %s", buf);
    close(fd);
    return 0;
}
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

Experimental results: 

         low.open.c creates a file and saves the file data, and returns file descriptor 3 after running. Use the cat command to output the file contents of data.txt to confirm that data is indeed transferred to the file.

        The low.read.c program reads the data saved in data.txt through the read function. After running, print the file descriptor and the contents of the file. 

        The fd_seri.c program creates both the file and the socket:

    fd1 = socket(PF_INET, SOCK_STREAM, 0);
    fd2 = open("test.dat", O_CREAT | O_WRONLY | O_TRUNC);
    fd3 = socket(PF_INET, SOCK_DGRAM, 0);

Then print their file descriptors respectively to get 3, 4, and 5. The descriptors are numbered in ascending order starting from 3, because 0, 1, and 2 are descriptors assigned to the I/O label. As shown below:

​​​​​​​

1.5 Exercises

1. What is the role of sockets in network programming? Why is it called a socket?

        A socket is a programming interface used to communicate over a network. Sockets allow processes on different computers to transfer data over a network. It provides a mechanism that allows computers to establish connections, send and receive data. Sockets enable applications to communicate over the network and exchange data between clients and servers.

        It is called a socket because it is analogous to a socket in telephone communications and serves as an endpoint connection point for network communications.

2. After creating the socket on the server side, the listen function and accept function will be called in sequence. Please compare and explain the functions of the two.

        The listen function is used to set the socket to passive listening mode to accept client connection requests.

        The accept function is used to accept the client's connection request and create a new socket to handle communication with the client.

3. In Linux, you can directly use file I/O related functions when performing I/O on socket data; but in Windows, you cannot. Why?

        This is because in Unix-like systems, including Linux, the idea that everything is a file is widely adopted. Various resources (including sockets) are abstracted into the form of file descriptors, and the data reading and writing interfaces are unified.

        However, in the Windows operating system, sockets are not considered file descriptors, so file I/O related functions cannot be used directly to read and write data.

4. After a socket is created, an address is usually assigned to it. Why? Which function needs to be called to complete the address assignment?

        In network programming, you need to assign an address to a socket after you create it, mainly so that other computers can find and establish connections with the socket. By assigning an address, you specify a socket's IP address and port number, thereby uniquely identifying the socket's location on the network.

        In order to complete the address allocation, the bind function needs to be called. The bind function is used to bind a socket to a specific IP address and port number.

Guess you like

Origin blog.csdn.net/m0_61028090/article/details/131446126