• 网络编程的学习


    思维导图

    多路复用代码练习

    select完成TCP并发服务器

    1. #include
    2. #define SER_IP "192.168.125.73" //服务器IP
    3. #define SER_PORT 8888 //服务器端口号
    4. int main(int argc, const char *argv[])
    5. {
    6. //1、创建用于监听的套接字
    7. int sfd = -1;
    8. sfd=socket(AF_INET, SOCK_STREAM, 0);
    9. //参数1:表示使用的是ipv4的通信
    10. //参数2:表示使用tcp通信类型
    11. //参数3:表示前面已经特定了通信协议
    12. if(sfd == -1)
    13. {
    14. perror("socket error");
    15. return -1;
    16. }
    17. printf("sfd = %d\n", sfd); //3
    18. //将端口号快速重用
    19. int reuse = 1;
    20. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    21. {
    22. perror("setsockopt error");
    23. return -1;
    24. }
    25. printf("端口号快速重用成功\n");
    26. //2、绑定ip地址和端口号
    27. //2.1填充地址信息结构体
    28. struct sockaddr_in sin;
    29. sin.sin_family = AF_INET; //地址族
    30. sin.sin_port = htons(SER_PORT); //端口号
    31. sin.sin_addr.s_addr = inet_addr(SER_IP); //IP地址
    32. //2.2绑定
    33. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
    34. {
    35. perror("bind error");
    36. return -1;
    37. }
    38. printf("bind success\n");
    39. //3、启动监听,允许客户端连接
    40. if(listen(sfd, 128) == -1)
    41. {
    42. perror("listen error");
    43. return -1;
    44. }
    45. printf("listen success\n");
    46. //4、当有客户端发来连接请求后创建新的用于通信的套接字
    47. //如果不想接收客户端地址信息结构体,则无需传入参数2和参数3
    48. //如果想要获取客户端地址信息结构体,则需要传入相关参数
    49. struct sockaddr_in cin; //用于接收客户端地址信息结构体
    50. socklen_t socklen = sizeof(cin); //用于接收客户端地址信息的大小
    51. int newfd = -1; //新创建用于通信的套接字文件描述符
    52. char sbuf[128] = ""; //从键盘上输入数据
    53. //1、创建文件描述符容器
    54. fd_set readfds, tempfds;
    55. //2、清空容器内容
    56. FD_ZERO(&readfds);
    57. //3、将sfd和0号文件描述符放入容器中
    58. FD_SET(0, &readfds);
    59. FD_SET(sfd, &readfds);
    60. int maxfd = sfd; //存放容器中最大的文件描述符的值
    61. struct sockaddr_in cin_arr[1024]; //存放客户端对应的地址信息
    62. while(1)
    63. {
    64. tempfds = readfds; //将要检测的容器复制保存一份
    65. int res = select(maxfd+1, &tempfds, NULL, NULL, NULL); //阻塞等待集合中事件产生
    66. if(res == -1)
    67. {
    68. perror("select error");
    69. return -1;
    70. }else if(res == 0)
    71. {
    72. printf("time out\n");
    73. return -1;
    74. }
    75. //遍历所有的容器中的文件描述符
    76. for(int i=0; i<=maxfd; i++)
    77. {
    78. //判断当前的文件描述符是否在tempfds中,如果不在,直接下一个
    79. if(!FD_ISSET(i, &tempfds))
    80. {
    81. continue;
    82. }
    83. //如果程序执行到此,说明检测的集合中的i文件描述符产生了事件
    84. if(i == sfd) //表示sfd触发了事件
    85. {
    86. newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
    87. if(newfd == -1)
    88. {
    89. perror("accept error");
    90. return -1;
    91. }
    92. printf("newfd = %d 您有新的客户已经上线\n", newfd);
    93. printf("客户端IP:%s, 端口号为:%d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
    94. //将客户端文件描述符放入到集合中
    95. FD_SET(newfd, &readfds);
    96. cin_arr[newfd] = cin; //将新的客户端地址信息放入容器
    97. //更新一下maxfd
    98. if(newfd > maxfd)
    99. {
    100. maxfd = newfd;
    101. }
    102. }else if(i == 0) //表示有终端输入事件
    103. {
    104. //从键盘输入数据
    105. fgets(sbuf, sizeof(sbuf), stdin);
    106. sbuf[strlen(sbuf)-1] = 0;
    107. printf("触发了键盘输入事件:%s\n", sbuf);
    108. //将消息发给每一个客户端
    109. for(int k=4; k<=maxfd; k++)
    110. {
    111. send(k, sbuf, strlen(sbuf), 0);
    112. }
    113. }else
    114. {
    115. //5、通信套接字与客户端进行数据收发
    116. char rbuf[128] = "";
    117. //清空容器
    118. bzero(rbuf, sizeof(rbuf));
    119. //从套接字中读取数据
    120. int res = recv(i, rbuf, sizeof(rbuf), 0);
    121. if(res == 0)
    122. {
    123. printf("客户端已经下线\n");
    124. //6、关闭套接字
    125. close(i);
    126. //将当前文件描述符从容器中移除
    127. FD_CLR(i, &readfds);
    128. //更新maxfd
    129. for(int j=maxfd; j>=0; j--)
    130. {
    131. if(FD_ISSET(j, &readfds))
    132. {
    133. maxfd = j;
    134. break;
    135. }
    136. }
    137. continue;
    138. }
    139. printf("[%s:%d] :%s\n", \
    140. inet_ntoa(cin_arr[i].sin_addr), ntohs(cin_arr[i].sin_port), rbuf);
    141. //将收到的消息加上其他字符回过去
    142. strcat(rbuf, "*_*");
    143. send(i, rbuf, strlen(rbuf), 0);
    144. }
    145. }
    146. }
    147. close(sfd);
    148. return 0;
    149. }

    poll实现TCP客户端

    1. #include
    2. #define SER_IP "192.168.125.73" //服务器ip
    3. #define SER_PORT 8888 //服务器端口号
    4. #define CLI_IP "192.168.125.73" //客户端IP
    5. #define CLI_PORT 9999 //客户端端口号
    6. int main(int argc, const char *argv[])
    7. {
    8. //1、创建用于通信的套接字文件描述符
    9. int cfd = socket(AF_INET, SOCK_STREAM, 0);
    10. if(cfd == -1)
    11. {
    12. perror("socket error");
    13. return -1;
    14. }
    15. printf("cfd = %d\n", cfd); //3
    16. //2、绑定(非必须)
    17. //2.1 填充地址信息结构体
    18. struct sockaddr_in cin;
    19. cin.sin_family = AF_INET;
    20. cin.sin_port = htons(CLI_PORT);
    21. cin.sin_addr.s_addr = inet_addr(CLI_IP);
    22. //2.2 绑定
    23. if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    24. {
    25. perror("bind error");
    26. return -1;
    27. }
    28. printf("bind success\n");
    29. //3、连接服务器
    30. //3.1填充要连接的服务器地址信息结构体
    31. struct sockaddr_in sin;
    32. sin.sin_family = AF_INET; //地址族
    33. sin.sin_port = htons(SER_PORT); //端口号
    34. sin.sin_addr.s_addr = inet_addr(SER_IP); //ip地址
    35. //3.2 连接服务器
    36. if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
    37. {
    38. perror("connect error");
    39. return -1;
    40. }
    41. printf("connect success\n");
    42. //使用poll完成客户端的读写操作并发执行
    43. //1、定义一个pollfd数组
    44. struct pollfd pfd[2];
    45. pfd[0].fd = 0; //检测0号文件描述符
    46. pfd[0].events = POLLIN; //检测0号文件描述符的读操作
    47. pfd[1].fd = cfd; //检测cfd文件描述符
    48. pfd[1].events = POLLIN; //检测cfd中的读操作
    49. //4、数据收发
    50. char wbuf[128] = "";
    51. while(1)
    52. {
    53. int res = poll(pfd, 2, -1); //阻塞检测集合中是否有事件产生
    54. if(res == -1)
    55. {
    56. perror("poll error");
    57. return -1;
    58. }else if(poll == 0)
    59. {
    60. printf("time out\n");
    61. return -1;
    62. }
    63. //程序执行至此,表示有事件产生
    64. if(pfd[0].revents == POLLIN) //表示0号文件描述符事件产生
    65. {
    66. //清空数据
    67. bzero(wbuf, sizeof(wbuf));
    68. printf("请输入>>>");
    69. fgets(wbuf, sizeof(wbuf), stdin); //从终端输入
    70. wbuf[strlen(wbuf)-1] = 0;
    71. //将数据发送给服务器
    72. send(cfd, wbuf, strlen(wbuf), 0);
    73. printf("发送成功\n");
    74. //判断发送的数据
    75. if(strcmp(wbuf, "quit") == 0)
    76. {
    77. break;
    78. }
    79. }
    80. if(pfd[1].revents == POLLIN) //表示cfd文件描述符事件产生
    81. {
    82. //接收服务器发来的消息
    83. //清空数据
    84. bzero(wbuf, sizeof(wbuf));
    85. recv(cfd, wbuf, sizeof(wbuf), 0);
    86. printf("收到消息为:%s\n", wbuf);
    87. }
    88. }
    89. //5、关闭套接字
    90. close(cfd);
    91. return 0;
    92. }

  • 相关阅读:
    安卓最佳实践之内存优化
    常规加密算法是什么?原理是怎么样?有哪些?
    Java笔记:阻塞队列
    基于 JuiceFS 的 KubeSphere DevOps 项目数据迁移方案
    知识图谱从入门到应用——知识图谱的存储与查询:基于原生图数据库的知识图谱存储
    Odoo:行业领先的免费开源财务管理解决方案
    Element学习使用
    罗景:连接效率优化实践
    ESP32网络开发实例-从SD卡加载Web页面文件
    sqlalchemy查询数据为空,查询范围对应的数据在数据库真实存在
  • 原文地址:https://blog.csdn.net/H_oUmU/article/details/136491257