MD小智

学习-思考-分享


  • 首页

  • 分类

  • 归档

  • 关于我

  • 搜索

自上而下理解内核网络(一)---TCP应用层介绍

时间: 2022-04-26   |   分类: Linux内核   | 字数: 1642 字 | 阅读: 4分钟 | 阅读次数:

本系列主要以TCP应用为指导,通过参考内核的网络实现,从应用层、传输层、网络层,自上而下的理解Linux内核网络设计,以加深对网络通讯内数据流传输路径的理解。

TCP协议简述

TCP协议的全称是Transmission Control Protocol,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通讯协议。在简化的计算机网络OSI模型中处于第四层指定的功能,UDP(用户数据报文协议)是同一层内另一个重要的传输协议。

OSI模型与TCP协议

TCP应用层通讯过程

一个简易的TCP服务端和客户端的通讯建立过程如下:

server_client.jpg

服务端

  1. 创建一个socket套接字;

    int socket(int domain, int type, int protocol);
    
  2. 创建一个struct sockaddr_in的变量serveraddr,将socket绑定的协议簇、IP、端口等参数完善;

    struct sockaddr_in serveraddr
    
  3. 绑定serveraddr到socket;

    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    
  4. 监听socket上的连接。这里listen()将 socket引用的套接字标记为被动套接字,即将用于使用 accept接收传入连接请求的套接字;

    int listen(int sockfd, int backlog);
    
  5. 等待TCP客户端的连接;

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  6. 当有TCP客户端与服务端建立连接后,accept会返回一个新的socket,在服务端通过对新的socket进行read/write操作,就可以与TCP客户端进行消息的接收/发送。

客户端

相比服务端的建立,客户端就比较简单。

  1. 创建一个socket套接字;

    int socket(int domain, int type, int protocol);
    
  2. 设置服务端的IP、端口到struct sockaddr *addr,与服务端建立连接;

    int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    
  3. 当连接成功后,通过socket``read/write操作,就可以与TCP客户端进行消息的接收/发送。

演示

我们在一台Ubuntu主机上编译演示代码中的server.c和client.c,然后在该主机上进行演示.

gcc server.c -o server
gcc client.c -o client
  1. 执行指令./server 8888运行服务端,端口号为8888;

  2. 执行指令./client 127.0.0.1 8888,让客户端连接本机的8888端口;

    ./client 127.0.0.1 8888
    Please enter msg:
    
  3. 此时可以看到服务端监听到新的连接请求;

    ./server 8888
    server established connection with localhost (127.0.0.1)
    
  4. 此时在客户端输入要发送的内容"tcp test",然后回车发送,发送完成后,可以看到服务端会进行回显。

    Please enter msg: tcp test
    Echo from server: tcp test
    
  5. 此时可以看到服务端接收到的信息:

    server received 9 bytes: tcp test
    
  6. 至此,服务端的读写演示完成。

演示代码

服务端

/*
 * tcpserver.c - A simple TCP echo server
 * usage: tcpserver <port>
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 1024


/*
 * error - wrapper for perror
 */
void error(char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char **argv)
{
    int parentfd;                  /* parent socket */
    int childfd;                   /* child socket */
    int portno;                    /* port to listen on */
    int clientlen;                 /* byte size of client's address */
    struct sockaddr_in serveraddr; /* server's addr */
    struct sockaddr_in clientaddr; /* client addr */
    struct hostent *hostp;         /* client host info */
    char buf[BUFSIZE];             /* message buffer */
    char *hostaddrp;               /* dotted decimal host addr string */
    int optval;                    /* flag value for setsockopt */
    int n;                         /* message byte size */

    /*
     * check command line arguments
     */
    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <port>\n", argv[0]);
        exit(1);
    }
    portno = atoi(argv[1]);

    /*
     * socket: create the parent socket
     */
    parentfd = socket(AF_INET, SOCK_STREAM, 0);
    if (parentfd < 0)
        error("ERROR opening socket");

    /* setsockopt: Handy debugging trick that lets
     * us rerun the server immediately after we kill it;
     * otherwise we have to wait about 20 secs.
     * Eliminates "ERROR on binding: Address already in use" error.
     */
    // optval = 1;
    // setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
    //            (const void *)&optval, sizeof(int));

    /*
     * build the server's Internet address
     */
    bzero((char *)&serveraddr, sizeof(serveraddr));

    /* this is an Internet address */
    serveraddr.sin_family = AF_INET;

    /* let the system figure out our IP address */
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    /* this is the port we will listen on */
    serveraddr.sin_port = htons((unsigned short)portno);

    /*
     * bind: associate the parent socket with a port
     */
    if (bind(parentfd, (struct sockaddr *)&serveraddr,
             sizeof(serveraddr)) < 0)
        error("ERROR on binding");

    /*
     * listen: make this socket ready to accept connection requests
     */
    if (listen(parentfd, 5) < 0) /* allow 5 requests to queue up */
        error("ERROR on listen");

    /*
     * main loop: wait for a connection request, echo input line,
     * then close connection.
     */
    clientlen = sizeof(clientaddr);
    while (1)
    {

        /*
         * accept: wait for a connection request
         */
        childfd = accept(parentfd, (struct sockaddr *)&clientaddr, &clientlen);
        if (childfd < 0)
            error("ERROR on accept");

        /*
         * gethostbyaddr: determine who sent the message
         */
        hostp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
                              sizeof(clientaddr.sin_addr.s_addr), AF_INET);
        if (hostp == NULL)
            error("ERROR on gethostbyaddr");
        hostaddrp = inet_ntoa(clientaddr.sin_addr);
        if (hostaddrp == NULL)
            error("ERROR on inet_ntoa\n");
        printf("server established connection with %s (%s)\n",
               hostp->h_name, hostaddrp);

        /*
         * read: read input string from the client
         */
        bzero(buf, BUFSIZE);
        n = read(childfd, buf, BUFSIZE);
        if (n < 0)
            error("ERROR reading from socket");
        printf("server received %d bytes: %s", n, buf);

        /*
         * write: echo the input string back to the client
         */
        n = write(childfd, buf, strlen(buf));
        if (n < 0)
            error("ERROR writing to socket");

        close(childfd);
    }
}

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define BUFSIZE 1024

/*
 * error - wrapper for perror
 */
void error(char *msg) {
    perror(msg);
    exit(0);
}

int main(int argc, char **argv) {
    int sockfd, portno, n;
    struct sockaddr_in serveraddr;
    struct hostent *server;
    char *hostname;
    char buf[BUFSIZE];

    /* check command line arguments */
    if (argc != 3) {
       fprintf(stderr,"usage: %s <hostname> <port>\n", argv[0]);
       exit(0);
    }
    hostname = argv[1];
    portno = atoi(argv[2]);

    /* socket: create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR opening socket");

    /* gethostbyname: get the server's DNS entry */
    server = gethostbyname(hostname);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host as %s\n", hostname);
        exit(0);
    }

    /* build the server's Internet address */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&serveraddr.sin_addr.s_addr, server->h_length);
    serveraddr.sin_port = htons(portno);

    /* connect: create a connection with the server */
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
      error("ERROR connecting");

    /* get message line from the user */
    printf("Please enter msg: ");
    bzero(buf, BUFSIZE);
    fgets(buf, BUFSIZE, stdin);

    /* send the message line to the server */
    n = write(sockfd, buf, strlen(buf));
    if (n < 0)
      error("ERROR writing to socket");

    /* print the server's reply */
    bzero(buf, BUFSIZE);
    n = read(sockfd, buf, BUFSIZE);
    if (n < 0)
      error("ERROR reading from socket");
    printf("Echo from server: %s", buf);
    close(sockfd);
    return 0;
}
#自上而下理解内核网络#
微信扫一扫关注

声明: 自上而下理解内核网络(一)---TCP应用层介绍

链接:https://mdxz2048.github.io/post/study_linux_tcp_in_linux_01/

作者:MD小智

声明: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0许可协议,转载请注明出处!

创作实属不易,如有帮助,那就打赏博主些许茶钱吧 ^_^
WeChat Pay

微信打赏

Alipay

支付宝打赏

Linux内核中系统调用过程分析
当我们在谈论阅读时,我们在谈论什么
  • 文章目录
  • 站点概览
MD小智

MD小智

我们会高估自己所拥有的一切!

39 日志
5 分类
48 标签
GitHub
标签云
  • 编码
  • 自上而下理解内核网络
  • 文件同步
  • 12306bypass
  • Asn.1
  • Asn1c编译器
  • Chatgpt
  • D触发器
  • Ean13
  • Gdb交叉编译
  • TCP协议简述
  • TCP应用层通讯过程
    • 服务端
    • 客户端
  • 演示
  • 演示代码
    • 服务端
    • 客户端
© 2010 - 2024 MD小智
Powered by - Hugo v0.96.0 / Theme by - NexT
/
Storage by GitHub / MD小智
0%