当前位置:   article > 正文

【TCP】socket套接字——快速上手

【TCP】socket套接字——快速上手

前言

socket套接字的有些知识在上一篇【UDP】socket套接字带你快速上手中已经讲过了,这里就不在过多的叙述了。

初识TCP

 TCP的主要特点

  1. TCP是面向连接的传输层协议
  2. TCP提供可靠交付的服务
  3. TCP提供全双工通信
  4. TCP是面向字节流的

TCP socket API 

 socket()

  • 此函数是用来创建socket文件描述符的。创建成功则返回socket文件描述符,失败则返回-1并且设置对应的错误码
  • 第一个参数是用来指定通信域的,也就是选择用于通信的协议族的,这里用的是IPv4协议,参数指定就是AF_INET
  • 第二个参数是用来指定通信类型的。这里所用到的是TCP,所以参数指定为SOCK_STREAM,表示面向流的传输协议
  • 第三个参数暂时用不到,设置为0即可

bind() 

  • 此函数是用来将用户设置的IP地址和port端口号在内核中和当前进程强关联。成功返回0,失败返回-1并且设置对应的错误码
  • 第一个参数是你创建好的socket文件描述符
  • 第二个参数是一个struct sockaddr的结构体指针
  • 第三个参数是这个sockaddr的大小

listen() 

  • 此函数用来声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略
  • listen成功返回0,失败返回-1并且设置对应的错误码

accept()

  • 三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到客户端连接上来
  • 第一个参数是创建好的socket文件描述符
  • 第二个参数是一个struct sockaddr的结构体指针

  • 第三个参数是此结构体的大小

  • 返回值为socket文件描述符

理解accept的返回值

accept中传入了一个socket文件描述符,但是它返回的依然是一个文件描述符,那么如何理解这两个文件描述符呢?

举个例子,假如你家有个饭店,刚好这时到了饭点了,你就派出了张三到饭店门口去拉客,此时来了一名顾客正好被张三拉进到店里去了,你又派出了李四服务员去服务这位顾客,并且让张三又返回到店门口继续拉客。

在上述例子中,张三就是我们传入accept中的socket文件描述符,是获取到的新连接的,而李四就是accept返回的socket文件描述符,是真正提供服务的。

 connect()

  • 在客户端中调用,用来连接服务器
  • 参数形式和bind的一致,区别在于bind传入的是自己的地址,为connect传入的是对方的地址
  • listen成功返回0,失败返回-1并且设置对应的错误码

tcp_client.cc

  1. #include <iostream>
  2. #include <string>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <unistd.h>
  6. #include <sys/socket.h>
  7. #include <sys/types.h>
  8. #include <arpa/inet.h>
  9. #include <netinet/in.h>
  10. void Usage(std::string proc)
  11. {
  12. std::cout << "\nUsage: " << proc << " serverIp serverPort\n"
  13. << std::endl;
  14. }
  15. int main(int argc, char *argv[])
  16. {
  17. if (argc != 3)
  18. {
  19. Usage(argv[0]);
  20. exit(1);
  21. }
  22. std::string serverip = argv[1];
  23. uint16_t serverport = atoi(argv[2]);
  24. bool alive = false;
  25. int sock = 0;
  26. std::string line;
  27. while (true)
  28. {
  29. if (!alive)
  30. {
  31. sock = socket(AF_INET, SOCK_STREAM, 0);
  32. if (sock < 0)
  33. {
  34. std::cerr << "socket error" << std::endl;
  35. exit(2);
  36. }
  37. struct sockaddr_in server;
  38. memset(&server, 0, sizeof(server));
  39. server.sin_family = AF_INET;
  40. server.sin_port = htons(serverport);
  41. server.sin_addr.s_addr = inet_addr(serverip.c_str());
  42. if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
  43. {
  44. std::cerr << "connect error" << std::endl;
  45. exit(3);
  46. }
  47. alive = true;
  48. std::cout << "connect success" << std::endl;
  49. }
  50. std::cout << "请输入你的内容# ";
  51. std::getline(std::cin, line);
  52. if (line == "quit")
  53. break;
  54. ssize_t s = send(sock, line.c_str(), line.size(), 0);
  55. if (s > 0)
  56. {
  57. char buffer[1024];
  58. ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
  59. if (s > 0)
  60. {
  61. buffer[s] = 0;
  62. std::cout << "server 回应# " << buffer << std::endl;
  63. }
  64. else if (s == 0)
  65. {
  66. close(sock);
  67. }
  68. }
  69. else
  70. {
  71. close(sock);
  72. }
  73. alive = false;
  74. }
  75. return 0;
  76. }

 tcp_server.cc

  1. #include "tcp_server.hpp"
  2. #include <memory>
  3. static void Usage(std::string proc)
  4. {
  5. std::cout << "\nUsage" << proc << " port\n" << std::endl;
  6. }
  7. int main(int argc, char* argv[])
  8. {
  9. if(argc != 2)
  10. {
  11. Usage(argv[0]);
  12. exit(1);
  13. }
  14. uint16_t port = atoi(argv[1]);
  15. std::unique_ptr<Tcp_Server> svr(new Tcp_Server(port));
  16. svr->InitServer();
  17. svr->Start();
  18. return 0;
  19. }

 tcp_server.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <cstring>
  5. #include <strings.h>
  6. #include <assert.h>
  7. #include <unistd.h>
  8. #include <signal.h>
  9. #include <sys/types.h>
  10. #include <sys/socket.h>
  11. #include <arpa/inet.h>
  12. #include <netinet/in.h>
  13. #include <pthread.h>
  14. #include <sys/wait.h>
  15. #include "log.hpp"
  16. static void service(int sock, const std::string &clientip, const uint16_t &clientport)
  17. {
  18. char buffer[1024];
  19. while (true)
  20. {
  21. ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
  22. if (s > 0)
  23. {
  24. buffer[s] = 0;
  25. std::cout << clientip << ":" << clientport << "# " << buffer << std::endl;
  26. }
  27. else if (s == 0) // 对端关闭连接
  28. {
  29. logMessage(NORMAL, "%s:%d shutdown, me too", clientip.c_str(), clientport);
  30. break;
  31. }
  32. else
  33. {
  34. logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
  35. break;
  36. }
  37. write(sock, buffer, strlen(buffer));
  38. }
  39. close(sock);
  40. }
  41. class ThreadDate
  42. {
  43. public:
  44. int _sock;
  45. std::string _ip;
  46. uint16_t _port;
  47. };
  48. class Tcp_Server
  49. {
  50. private:
  51. const static int gbacklog = 20;
  52. static void *threadRoutine(void *args)//必须得是static,因为不是static的话会多出一个this指针
  53. {
  54. pthread_detach(pthread_self());//线程分离
  55. ThreadDate *td = static_cast<ThreadDate*> (args);
  56. service(td->_sock, td->_ip, td->_port);
  57. delete td;
  58. return nullptr;
  59. }
  60. public:
  61. Tcp_Server(uint16_t port, std::string ip = "")
  62. : _port(port), _ip(ip), listensock(-1)
  63. {
  64. }
  65. ~Tcp_Server()
  66. {
  67. }
  68. void InitServer()
  69. {
  70. // 1.创建socket
  71. listensock = socket(AF_INET, SOCK_STREAM, 0);
  72. if (listensock < 0)
  73. {
  74. //创建socket失败
  75. logMessage(FATAL, "create socket error, %d:%S", errno, strerror(errno));
  76. exit(2);
  77. }
  78. logMessage(NORMAL, "create socket success, listensock:%d", listensock);
  79. // 2.bind
  80. struct sockaddr_in local;
  81. memset(&local, 0, sizeof local);
  82. local.sin_family = AF_INET;
  83. local.sin_port = htons(_port);
  84. local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); //INADDR_ANY:任意IP
  85. if (bind(listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
  86. {
  87. //bind失败
  88. logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
  89. exit(3);
  90. }
  91. // 3.建立连接
  92. if (listen(listensock, gbacklog) < 0)
  93. {
  94. //建立连接失败
  95. logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
  96. exit(4);
  97. }
  98. logMessage(NORMAL, "init server success");
  99. }
  100. void Start()
  101. {
  102. // signal(SIGCHLD, SIG_IGN);//多进程版
  103. while (true)
  104. {
  105. // 4.获取连接
  106. struct sockaddr_in src;
  107. socklen_t len = sizeof(src);
  108. int servicesock = accept(listensock, (struct sockaddr *)&src, &len);
  109. // 获取连接失败
  110. if (servicesock < 0)
  111. {
  112. logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
  113. continue;
  114. }
  115. // 获取连接成功
  116. uint16_t client_port = ntohs(src.sin_port);
  117. std::string client_ip = inet_ntoa(src.sin_addr);
  118. logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n", servicesock, client_ip.c_str(), client_port);
  119. // 5.开始通信
  120. // 版本1——单进程循环版
  121. // 缺点:一次只能处理一个客户端,处理完后才能处理下一个
  122. // service(servicesock, client_ip, client_port);
  123. // close(servicesock);
  124. //版本2.0——多进程(创建子进程)
  125. // pid_t id = fork();
  126. // assert(id != -1);
  127. // if(id == 0)
  128. // {
  129. //子进程
  130. // close(listensock);
  131. // service(servicesock, client_ip, client_port);
  132. // exit(0);
  133. // }
  134. //父进程
  135. // close(servicesock);
  136. //版本2.1——多进程版本(孤儿进程)
  137. // pid_t id = fork();
  138. // assert(id != -1);
  139. // if(id == 0)
  140. // {
  141. //子进程
  142. // close(listensock);
  143. // if(fork() > 0) exit(0);//子进程退出,由操作系统领养
  144. //孙子进程
  145. // service(servicesock, client_ip, client_port);
  146. // exit(0);
  147. // }
  148. //父进程
  149. // waitpid(id, nullptr, 0);
  150. // close(servicesock);
  151. //版本3——多线程版
  152. ThreadDate *td = new ThreadDate();
  153. td->_sock = servicesock;
  154. td->_ip = client_ip;
  155. td->_port = client_port;
  156. pthread_t tid;
  157. pthread_create(&tid, nullptr, threadRoutine, td);
  158. }
  159. }
  160. private:
  161. uint16_t _port;
  162. std::string _ip;
  163. int listensock;
  164. };

完整代码链接

TCP socket:TCP Socket 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/451923
推荐阅读
相关标签
  

闽ICP备14008679号