• Linux 网络编程 tcp server 笔记


    一、TCP 服务器的创建

    Linux 上创建一个简单的 tcp 服务器步骤如下:

    ①创建套接字

    ②将套接字绑定到 IP 地址和端口号

    ③监听来自客户端的连接

    ④接受连接并创建新的套接字用于与客户端通信

    ⑤通过新建的套接字发送和接收数据

    ⑥关闭套接字

    流程框图如下:

    7e06856095275adf66b6fb22f68a0c3b.png

    根据以上介绍可以创建tcp server的示例,分为服务器-单客户端和服务器-多客户端。

    二、服务器-单客户端示例

    tcp server示例代码如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include //struct sockaddr_in
    6. #include //inet_addr()
    7. #include //close()
    8. #define MY_PRINTF(argv) do{\
    9. printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
    10. printf("%s\r\n",argv);\
    11. }while(0);
    12. #define MYPORT 5000
    13. //tcp server demo
    14. int main(int argc,char *argv[])
    15. {
    16. int sockefd;
    17. int sockenewfd;
    18. int ret;
    19. int enable=1;
    20. struct sockaddr_in my_addr;//本地地址-服务器
    21. struct sockaddr_in remote_addr;//远端地址-客户端
    22. int remote_addr_len;
    23. char buf[1024];
    24. sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
    25. if(sockefd<0)
    26. {
    27. MY_PRINTF("socket err !! ");
    28. return -1;
    29. }
    30. if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1)
    31. {
    32. close(sockefd);
    33. MY_PRINTF(" setsockopt err !! ");
    34. return -1;
    35. }
    36. MY_PRINTF("socket ok !! ");
    37. //本地地址
    38. my_addr.sin_family=AF_INET;
    39. my_addr.sin_port=htons(MYPORT);//0:随机端口
    40. my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip // inet_addr():IP 地址的字符串转换成一个无符号长整型
    41. bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));
    42. ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定 //转换数据类型
    43. if(ret<0)
    44. {
    45. close(sockefd);
    46. MY_PRINTF("bind err !! ");
    47. return -1;
    48. }
    49. MY_PRINTF("bind ok !! ");
    50. ret=listen(sockefd,5);//监听
    51. if(ret<0)
    52. {
    53. close(sockefd);
    54. MY_PRINTF("listen err !! ");
    55. return -1;
    56. }
    57. MY_PRINTF("listen ok !! ");
    58. sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
    59. if(sockenewfd<0)
    60. {
    61. close(sockefd);
    62. MY_PRINTF("accept err !! ");
    63. return -1;
    64. }
    65. MY_PRINTF("accept ok !! ");
    66. printf("%d\r\n",ntohs(remote_addr.sin_port)); //端口号
    67. printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址 //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来
    68. for(;;)
    69. {
    70. ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
    71. if(ret<0)
    72. {
    73. close(sockenewfd);
    74. MY_PRINTF("recv err !! ");
    75. break;
    76. }
    77. else if(ret==0)
    78. {
    79. close(sockenewfd);
    80. MY_PRINTF("close !! ");
    81. perror("close sockenewfd");
    82. break;
    83. }
    84. MY_PRINTF("recv ok !! ");
    85. MY_PRINTF(buf);
    86. ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
    87. if(ret<0)
    88. {
    89. MY_PRINTF("send err !! ");
    90. }
    91. MY_PRINTF("send ok !! ");
    92. MY_PRINTF(buf);
    93. }
    94. close(sockefd);//关闭
    95. perror("close sockefd");
    96. return0;
    97. }

    程序运行只允许单个客户端连接通讯。测试如下:

    服务端启动,等待连接,如下:

    50959d4753910442cafe7bf93bfb25fa.png

    客户端连接,如下:

    9c044a0cc3c0414d814cab194431c141.png

    服务端获取到客户端连接,如下:

    499390968ee107bb269ebe68b9b98bce.png

    客户端发送数据,并接收到服务端返回,如下:

    fe6c0bd29696559425a8169907429e7e.png

    服务端显示:

    bddbe154b449fa7b1c1eb62af765d2fc.png

    三、服务器-多客户端

    这里介绍两种方法,方法一:使用多线程;方法二:使用select方法。

    ①方法一多线程,测试代码如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include //struct sockaddr_in
    6. #include //inet_addr()
    7. #include //close()
    8. #define MY_PRINTF(argv) do{\
    9. printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
    10. printf("%s\r\n",argv);\
    11. }while(0);
    12. #define MYPORT 5000
    13. //tcp server demo
    14. void *myfun_thread(void *socketInfo)
    15. {
    16. char buf[1024];
    17. int ret;
    18. int sockenewfd=(int)socketInfo;
    19. for(;;)
    20. {
    21. ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
    22. if(ret<0)
    23. {
    24. close(sockenewfd);
    25. MY_PRINTF("recv err !! ");
    26. perror("close sockenewfd");
    27. break;
    28. }
    29. else if(ret==0)
    30. {
    31. close(sockenewfd);
    32. MY_PRINTF("close !! ");
    33. perror("close sockenewfd");
    34. break;
    35. }
    36. MY_PRINTF("recv ok !! ");
    37. MY_PRINTF(buf);
    38. ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
    39. if(ret<0)
    40. {
    41. MY_PRINTF("send err !! ");
    42. }
    43. MY_PRINTF("send ok !! ");
    44. MY_PRINTF(buf);
    45. }
    46. pthread_exit(NULL);
    47. }
    48. int main(int argc,char *argv[])
    49. {
    50. int sockefd;
    51. int sockenewfd;
    52. int ret;
    53. int enable=1;
    54. pthread_t threadRx;
    55. struct sockaddr_in my_addr;//本地地址-服务器
    56. struct sockaddr_in remote_addr;//远端地址-客户端
    57. int remote_addr_len;
    58. sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
    59. if(sockefd<0)
    60. {
    61. MY_PRINTF("socket err !! ");
    62. return -1;
    63. }
    64. if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1)
    65. {
    66. close(sockefd);
    67. MY_PRINTF(" setsockopt err !! ");
    68. return -1;
    69. }
    70. MY_PRINTF("socket ok !! ");
    71. //本地地址
    72. my_addr.sin_family=AF_INET;
    73. my_addr.sin_port=htons(MYPORT);//0:随机端口
    74. my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip // inet_addr():IP 地址的字符串转换成一个无符号长整型
    75. bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));
    76. ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定 //转换数据类型
    77. if(ret<0)
    78. {
    79. close(sockefd);
    80. MY_PRINTF("bind err !! ");
    81. return -1;
    82. }
    83. MY_PRINTF("bind ok !! ");
    84. ret=listen(sockefd,5);//监听
    85. if(ret<0)
    86. {
    87. close(sockefd);
    88. MY_PRINTF("listen err !! ");
    89. return -1;
    90. }
    91. MY_PRINTF("listen ok !! ");
    92. while(1)
    93. {
    94. sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
    95. if(sockenewfd<0)
    96. {
    97. MY_PRINTF("accept err !! ");
    98. break;
    99. }
    100. MY_PRINTF("accept ok !! ");
    101. printf("%d\r\n",ntohs(remote_addr.sin_port)); //端口号
    102. printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址 //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来
    103. pthread_create(&threadRx, NULL, myfun_thread, (void *)sockenewfd);
    104. sleep(1);
    105. }
    106. close(sockefd);//关闭
    107. perror("close sockefd");
    108. return 0;
    109. }

    测试结果:

    服务端:

    2eedde70a977fc5d02db9ac76f735503.png

    客户端:

    5cf7c32b0cfbd080d17623fc87289453.png

  • 相关阅读:
    [QCM6125][Android13] 默认允许使用usb权限
    nodejs+vue+elementui办公用品电商家具网站python
    Oracle-数据库对象的使用
    基于YOLOv8模型的深海鱼目标检测系统(PyTorch+Pyside6+YOLOv8模型)
    微模块-前端业务模块化探索,拆解巨石应用的又一利器
    LVGL_基础控件线条line
    Redis Desktop Manager可视化工具
    单元测试的重要性
    什么是Spring
    Windows(二):windows+nginx+openssl本地搭建nginx并配置ssl实现https访问
  • 原文地址:https://blog.csdn.net/weixin_46158019/article/details/133759258