当前位置:   article > 正文

【嵌入式开发之网络编程】TCP并发实现

【嵌入式开发之网络编程】TCP并发实现

在实例中,经常会存在多个客户端向服务端发送连接请求和传递数据的情况,为了解决这个问题,我们需要利用多进程或者多线程来实现并发。

TCP多进程并发

要想实现TCP多进程并发,就要从accept函数下手,每次接收到来自客户端的请求,都生成一个新的文件描述符,记录下客户端的地址信息(IP和端口号),然后通过新文件描述符用read函数读取传送的内容。

  1. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  2. - 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
  3. - 参数:
  4. - sockfd : 用于监听的文件描述符
  5. - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
  6. - addrlen : 指定第二个参数的对应的内存大小
  7. - 返回值:
  8. - 成功 :用于通信的文件描述符
  9. - -1 : 失败

服务端实现代码

客户端代码与【嵌入式开发之网络编程】Socket套接字及TCP通信的实现中一致,关于进程创建和回收,参考【嵌入式开发之并发程序设计】进程的创建和回收

  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <sys/types.h>
  4. #include <stdlib.h>
  5. #include <arpa/inet.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <strings.h>
  9. #include <signal.h>
  10. #include <sys/wait.h>
  11. #define BACKLOG 5
  12. void SigHandle(int sig) {
  13. if (sig == SIGCHLD) {
  14. printf("client exited\n");
  15. wait(NULL);
  16. }
  17. }
  18. void ClientHandle(int newfd);
  19. int main(int argc, const char *argv[])
  20. {
  21. int fd, newfd;
  22. struct sockaddr_in addr, clint_addr;
  23. socklen_t addrlen = sizeof(clint_addr);
  24. #if 1
  25. struct sigaction act;
  26. act.sa_handler = SigHandle;
  27. act.sa_flags = SA_RESTART;
  28. sigemptyset(&act.sa_mask);
  29. sigaction(SIGCHLD, &act, NULL);
  30. #else
  31. signal(SIGCHLD, SigHandle);
  32. #endif
  33. pid_t pid;
  34. if (argc < 3) {
  35. fprintf(stderr, "%s<addr><port>\n", argv[0]);
  36. exit(0);
  37. }
  38. /*创建套接字
  39. * 使用IPv4互联网协议
  40. * 流式套间字
  41. * 协议唯一对应*/
  42. if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  43. perror("socket");
  44. exit(0);
  45. }
  46. addr.sin_family = AF_INET;
  47. addr.sin_port = htons(atoi(argv[2]));
  48. #if 0
  49. addr.sin_addr.s_addr = inet_addr(argv[1]);
  50. #else
  51. if (inet_aton(argv[1], &addr.sin_addr) == 0) {
  52. fprintf(stderr, "Invalid address\n");
  53. exit(EXIT_FAILURE);
  54. }
  55. #endif
  56. //地址快速重用
  57. int flag = 1, len = sizeof(int);
  58. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) {
  59. perror("setsockopt");
  60. exit(1);
  61. }
  62. //绑定通信结构体
  63. if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
  64. perror("bind");
  65. exit(0);
  66. }
  67. //设置套接字为监听模式
  68. if (listen(fd, BACKLOG) == -1) {
  69. perror("listen");
  70. exit(0);
  71. }
  72. while(1) {
  73. //接受客户端的连接请求,生成新的和客户端通信的套间字
  74. if ((newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen)) < 0) {
  75. perror("accept");
  76. exit(0);
  77. }
  78. /*IP地址转换成点分十进制
  79. * 端口号网络字节序转成主机字节序
  80. * 生成子进程,并读取数据*/
  81. printf("addr:%s port:%d\n", inet_ntoa(clint_addr.sin_addr), ntohs(clint_addr.sin_port));
  82. if ((pid = fork()) < 0) {
  83. perror("fork");
  84. exit(0);
  85. } else if (pid == 0) {
  86. close(fd);
  87. ClientHandle(newfd);
  88. exit(0);
  89. } else {
  90. close(newfd);
  91. }
  92. }
  93. close(fd);
  94. return 0;
  95. }
  96. void ClientHandle(int newfd) {
  97. int ret;
  98. char buf[BUFSIZ] = {};
  99. while(1){
  100. memset(buf, 0, BUFSIZ);
  101. ret = read(newfd, buf, BUFSIZ);
  102. if (ret < 0){
  103. perror("read");
  104. exit(0);
  105. } else if (ret == 0) {
  106. break;
  107. } else {
  108. printf("buf = %s\n", buf);
  109. }
  110. }
  111. close(newfd);
  112. }

运行结果(服务端)

  1. $ ./server 0 8888
  2. addr:127.0.0.1 port:54564
  3. buf = woeoeoe
  4. addr:127.0.0.1 port:47698
  5. buf = woeooe

TCP多线程并发

服务端实现代码

这部分涉及到线程的创建,参考【嵌入式开发之并发程序设计】线程的基本概念、pthread线程库的创建、互斥锁的使用、条件变量以及线程池的概念和使用

  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <sys/types.h>
  4. #include <stdlib.h>
  5. #include <arpa/inet.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <strings.h>
  9. #include <pthread.h>
  10. #define BACKLOG 5
  11. void *ClinetHandle(void *arg);
  12. int main(int argc, char *argv[])
  13. {
  14. int fd, newfd;
  15. struct sockaddr_in addr, clint_addr;
  16. pthread_t tid;
  17. socklen_t addrlen = sizeof(clint_addr);
  18. if(argc < 3){
  19. fprintf(stderr, "%s<addr><port>\n", argv[0]);
  20. exit(0);
  21. }
  22. /*创建套接字*/
  23. fd = socket(AF_INET, SOCK_STREAM, 0);
  24. if(fd < 0){
  25. perror("socket");
  26. exit(0);
  27. }
  28. addr.sin_family = AF_INET;
  29. addr.sin_port = htons( atoi(argv[2]) );
  30. if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
  31. fprintf(stderr, "Invalid address\n");
  32. exit(EXIT_FAILURE);
  33. }
  34. /*地址快速重用*/
  35. int flag=1,len= sizeof (int);
  36. if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) {
  37. perror("setsockopt");
  38. exit(1);
  39. }
  40. /*绑定通信结构体*/
  41. if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
  42. perror("bind");
  43. exit(0);
  44. }
  45. /*设置套接字为监听模式*/
  46. if(listen(fd, BACKLOG) == -1){
  47. perror("listen");
  48. exit(0);
  49. }
  50. while(1){
  51. /*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
  52. newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
  53. if(newfd < 0){
  54. perror("accept");
  55. exit(0);
  56. }
  57. printf("addr:%s port:%d\n", inet_ntoa(clint_addr.sin_addr), ntohs(clint_addr.sin_port) );
  58. /*创建线程,并设置分离属性*/
  59. pthread_create(&tid, NULL, ClinetHandle, &newfd);
  60. pthread_detach(tid);
  61. }
  62. close(fd);
  63. return 0;
  64. }
  65. void *ClinetHandle(void *arg){
  66. int ret;
  67. char buf[BUFSIZ] = {};
  68. int newfd = *(int *)arg;
  69. while(1){
  70. //memset(buf, 0, BUFSIZ);
  71. bzero(buf, BUFSIZ);
  72. ret = read(newfd, buf, BUFSIZ);
  73. if(ret < 0)
  74. {
  75. perror("read");
  76. exit(0);
  77. }
  78. else if(ret == 0)
  79. break;
  80. else
  81. printf("buf = %s\n", buf);
  82. }
  83. printf("client exited\n");
  84. close(newfd);
  85. return NULL;
  86. }

运行结果(服务端)

  1. $ ./server 0 8888
  2. addr:127.0.0.1 port:59782
  3. buf = Hello world
  4. addr:127.0.0.1 port:34552
  5. buf = haode
  6. client exited
  7. client exited
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号