• l8-d7 实现TCP通信


    一、TCP服务器的实现(理论)

    #include

    #include

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

            -domain: 指定通信域(通信地址族);

            -type: 指定套接字类型;

            -protocol: 指定协议;

    套接字类型与协议

    -type: 指定套接字类型

    TCP唯一对应流式套接字,所以选择SOCK_STREAM(数据报套接字:SOCK_DGRAM)

    -protocol: 指定协议

    流式套接字唯一对应TCP,所以无需要指定协议,设为0即可

    bind函数与通信结构体

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

            -sockfd:socket函数生成的套接字

            -addr:通信结构体

            -addrlen:通信结构体的长度

    IPv4地址族结构体

    struct sockaddr_in {

            sa_family_t    sin_family; /* 地址族: AF_INET */

            in_port_t      sin_port;   /* 网络字节序的端口号 */

            struct in_addr sin_addr;   /*IP地址结构体 */

    };

    /* IP地址结构体 */

    struct in_addr {

            uint32_t       s_addr;     /* 网络字节序的IP地址 */

    };

    二、实现代码

    优化前

    服务端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define PORT 5001
    8. #define BACKLOG 5
    9. int main(int argc, char *argv[])
    10. {
    11. int fd, newfd;
    12. char buf[BUFSIZ] = {}; //BUFSIZ 8142
    13. struct sockaddr_in addr;
    14. /*创建套接字*/
    15. fd = socket(AF_INET, SOCK_STREAM, 0);
    16. if(fd < 0){
    17. perror("socket");
    18. exit(0);
    19. }
    20. addr.sin_family = AF_INET;
    21. addr.sin_port = htons(PORT);
    22. addr.sin_addr.s_addr = 0;
    23. /*绑定通信结构体*/
    24. if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
    25. perror("bind");
    26. exit(0);
    27. }
    28. /*设置套接字为监听模式*/
    29. if(listen(fd, BACKLOG) == -1){
    30. perror("listen");
    31. exit(0);
    32. }
    33. /*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
    34. newfd = accept(fd, NULL, NULL);
    35. if(newfd < 0){
    36. perror("accept");
    37. exit(0);
    38. }
    39. printf("BUFSIZ = %d\n", BUFSIZ);
    40. read(newfd, buf, BUFSIZ);
    41. printf("buf = %s\n", buf);
    42. close(fd);
    43. return 0;
    44. }

    客户端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define PORT 5001
    8. #define BACKLOG 5
    9. #define STR "Hello World!"
    10. int main(int argc, char *argv[])
    11. {
    12. int fd;
    13. struct sockaddr_in addr;
    14. /*创建套接字*/
    15. fd = socket(AF_INET, SOCK_STREAM, 0);
    16. if(fd < 0){
    17. perror("socket");
    18. exit(0);
    19. }
    20. addr.sin_family = AF_INET;
    21. addr.sin_port = htons(PORT);
    22. addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    23. /*向服务端发起连接请求*/
    24. if(connect(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
    25. perror("connect");
    26. exit(0);
    27. }
    28. write(fd, STR, sizeof(STR) );
    29. printf("STR = %s\n", STR);
    30. close(fd);
    31. return 0;
    32. }

    优化后

    服务端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define BACKLOG 5 //BACKLOG为5,表示服务器端可以排队的最大客户端连接数
    9. int main(int argc, char *argv[])
    10. {
    11. int fd, newfd, ret;
    12. char buf[BUFSIZ] = {}; //BUFSIZ 8142 用于存储从客户端读取的数据
    13. struct sockaddr_in addr; //存储服务器的地址信息
    14. if(argc < 3){ //检查命令行参数的数量是否小于3,如果是,则打印错误信息并退出程序。这是因为服务器需要两个命令行参数:服务器的IP地址和端口号。
    15. fprintf(stderr, "%s\n", argv[0]);
    16. exit(0);
    17. }
    18. /*创建套接字*/
    19. fd = socket(AF_INET, SOCK_STREAM, 0); //创建一个新的套接字
    20. if(fd < 0){ //AF_INET表示使用IPv4协议,SOCK_STREAM表示使用TCP协议
    21. perror("socket");
    22. exit(0);
    23. }
    24. addr.sin_family = AF_INET; //使用IPv4协议
    25. addr.sin_port = htons( atoi(argv[2]) ); //将命令行参数中的端口号转换为整数,并使用htons函数将其转换为网络字节序,然后设置到addr结构体中的sin_port字段。
    26. if ( inet_aton(argv[1], &addr.sin_addr) == 0) { //将命令行参数中的IP地址转换为网络字节序并设置到addr结构体中的sin_addr字段。如果转换失败,则打印错误信息并退出程序。
    27. fprintf(stderr, "Invalid address\n");
    28. exit(EXIT_FAILURE);
    29. }
    30. /*绑定通信结构体*/
    31. if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){ //调用bind函数将套接字fd绑定到地址信息addr
    32. perror("bind");
    33. exit(0);
    34. }
    35. /*设置套接字为监听模式*/
    36. if(listen(fd, BACKLOG) == -1){ //用listen函数将套接字fd设置为监听模式,并设置最大客户端连接数为BACKLOG
    37. perror("listen");
    38. exit(0);
    39. }
    40. /*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
    41. newfd = accept(fd, NULL, NULL); //调用accept函数接受客户端的连接请求,并返回一个新的套接字描述符newfd用于和客户端通信
    42. if(newfd < 0){
    43. perror("accept");
    44. exit(0);
    45. }
    46. while(1){
    47. memset(buf, 0, BUFSIZ); //调用memset函数将buf数组中的所有元素都设置为0
    48. ret = read(newfd, buf, BUFSIZ); //调用read函数从客户端套接字newfd读取数据,并将数据存储到buf数组中。read函数返回读取到的字节数,并将其存储到ret变量中
    49. if(ret < 0) //如果read函数返回值小于0,表示读取数据失败
    50. {
    51. perror("read");
    52. exit(0);
    53. }
    54. else if(ret == 0) //如果read函数返回值等于0,表示客户端已经关闭了连接
    55. break;
    56. else
    57. printf("buf = %s\n", buf);
    58. }
    59. close(newfd); //关闭客户端套接字和服务器套接字
    60. close(fd);
    61. return 0;
    62. }

    客户端

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define BACKLOG 5
    9. int main(int argc, char *argv[])
    10. {
    11. int fd;
    12. struct sockaddr_in addr;
    13. char buf[BUFSIZ] = {};
    14. if(argc < 3){
    15. fprintf(stderr, "%s\n", argv[0]);
    16. exit(0);
    17. }
    18. /*创建套接字*/
    19. fd = socket(AF_INET, SOCK_STREAM, 0);
    20. if(fd < 0){
    21. perror("socket");
    22. exit(0);
    23. }
    24. addr.sin_family = AF_INET;
    25. addr.sin_port = htons( atoi(argv[2]) );
    26. if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
    27. fprintf(stderr, "Invalid address\n");
    28. exit(EXIT_FAILURE);
    29. }
    30. /*向服务端发起连接请求*/
    31. if(connect(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){ //调用connect函数向服务器发起连接请求
    32. perror("connect");
    33. exit(0);
    34. }
    35. while(1){
    36. printf("Input->");
    37. fgets(buf, BUFSIZ, stdin); //调用fgets函数从标准输入读取一行数据,并将其存储到buf数组中。
    38. write(fd, buf, strlen(buf) ); //调用write函数将buf数组中的数据发送给服务器。
    39. }
    40. close(fd);
    41. return 0;
    42. }

    makefile 

    CC=gcc
    CFLAGS=-Wall
    all:client server

    clean:
        rm client server

  • 相关阅读:
    C 语言宏 + 内联汇编实现 MIPS 系统调用
    栈和队列相关的一些问题
    机械设计中的结构要素、基本要求和准则
    upp(统一流程平台)项目,如果需要项目章程会怎么写
    双链笔记葫芦笔记综合评测:优点、缺点、建议
    Dubbo详解,用心看这一篇文章就够了【重点】
    2023-09-23力扣每日一题
    不动产数据质量提升_电子档案挂接
    uniapp 封装request请求
    [PHP]关联和操作MySQL数据库然后将数据库部署到ECS
  • 原文地址:https://blog.csdn.net/revengeman/article/details/132699083