communication_tcp.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include "communication_tcp.h"
  2. #include "string.h"
  3. Communication_tcp::Communication_tcp()
  4. {
  5. }
  6. Communication_tcp::~Communication_tcp()
  7. {
  8. }
  9. bool Communication_tcp::communication_tcp_init() //创建套接字
  10. {
  11. //创建socket (TCP/IP协议 TCP)
  12. m_socket = socket(AF_INET, SOCK_STREAM, 0); //直接创建socket返回给Communication_tcp的成员
  13. if (m_socket == -1)
  14. {
  15. printf(" create tcp socket failed ");
  16. return false;
  17. }
  18. else
  19. {
  20. //printf(" create tcp socket successed ");
  21. return true;
  22. }
  23. }
  24. bool Communication_tcp::communication_tcp_bind(const char *ip,unsigned short port) //绑定并监听端口号(服务端用)
  25. {
  26. sockaddr_in saddr; //数据结构
  27. saddr.sin_family = AF_INET; //协议
  28. saddr.sin_port = htons(port); //端口,主机字节序(小端方式)转换成网络字节序(大端方式)
  29. saddr.sin_addr.s_addr = inet_addr(ip); //绑定IP
  30. if (bind(m_socket, (sockaddr*)&saddr, sizeof(saddr)) != 0)
  31. {
  32. printf(" tcp bind ip:%s port:%d failed\n",ip,port);
  33. return false;
  34. }
  35. printf(" tcp bind ip:%s port:%d success\n",ip,port);
  36. return true;
  37. }
  38. bool Communication_tcp::communication_tcp_listen(unsigned short num) //最大连接数
  39. {
  40. int re = listen(m_socket, num); //套接字,最大请求队列的长度 进入阻塞状态
  41. if(!re)
  42. {
  43. printf(" tcp socket listen start ");
  44. return true;
  45. }
  46. else
  47. {
  48. printf(" tcp socket listen failed ");
  49. return false;
  50. }
  51. }
  52. bool Communication_tcp::set_block(bool isblock) //设置阻塞模式 (希望只有在connect的时候是非阻塞的,而接收数据时候是阻塞的)
  53. {
  54. if (m_socket <= 0)
  55. {
  56. printf(" set tcp socket block failed\n ");
  57. return false;
  58. }
  59. int flags = fcntl(m_socket, F_GETFL, 0); //获取socket的属性
  60. if (flags < 0)
  61. return false; //获取属性出错
  62. if (isblock)
  63. {
  64. flags = flags&~O_NONBLOCK; //把非阻塞这位设为0
  65. }
  66. else
  67. {
  68. flags = flags | O_NONBLOCK; //把非阻塞这位设为1
  69. }
  70. if (fcntl(m_socket, F_SETFL, flags))
  71. return false; //把标准位设回去
  72. // if (!isblock)
  73. // printf("set tcp socket not block success\n");
  74. // if (isblock)
  75. // printf("set tcp socket block success\n");
  76. return true;
  77. }
  78. bool Communication_tcp::communication_tcp_connect(const char *ip, unsigned short port , int sec)
  79. {
  80. if (m_socket <= 0)
  81. return false;
  82. sockaddr_in saddr; //设置连接对象的结构体
  83. saddr.sin_family = AF_INET;
  84. saddr.sin_port = htons(port);
  85. saddr.sin_addr.s_addr = inet_addr(ip); //字符串转整型
  86. set_block(false); //将socket改成非阻塞模式,此时它会立即返回 所以通过fd_set
  87. fd_set rfds, wfds; //文件句柄数组,在这个数组中,存放当前每个文件句柄的状态
  88. if (connect(m_socket, (sockaddr*)&saddr, sizeof(saddr)) != 0) //此时connect马上返回,状态为未成功连接
  89. {
  90. FD_ZERO(&rfds); //首先把文件句柄的数组置空
  91. FD_ZERO(&wfds);
  92. FD_SET(m_socket, &rfds); //把sock的网络句柄加入到该句柄数组中
  93. FD_SET(m_socket, &wfds);
  94. timeval tm; //超时参数的结构体
  95. tm.tv_sec = sec;
  96. tm.tv_usec = 0;
  97. int selres = select(m_socket + 1, &rfds, &wfds, NULL, &tm); //(阻塞函数)(监听的文件句柄的最大值加1,可读序列文件列表,可写的序列文件列表,错误处理,超时)使用select监听文件序列set是否有可读可写,这里监听set数组(里面只有sock),只要其中的句柄有一个变得可写(在这里是sock连接成功了以后就会变得可写,就返回),就返回
  98. switch (selres)
  99. {
  100. case -1:
  101. printf("select error\n");
  102. return false;
  103. case 0:
  104. printf("select time out\n");
  105. return false;
  106. default:
  107. if (FD_ISSET(m_socket, &rfds) || FD_ISSET(m_socket, &wfds))
  108. {
  109. connect(m_socket, (sockaddr*)&saddr, sizeof(saddr)); //再次连接一次进行确认
  110. int err = errno;
  111. if (err == EISCONN||err == EINPROGRESS) //已经连接到该套接字 或 套接字为非阻塞套接字,且连接请求没有立即完成
  112. {
  113. //printf("connect %s : %d finished(success).\n",ip,port);
  114. set_block(true); //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
  115. return true;
  116. }
  117. else
  118. {
  119. printf("connect %s : %d finished(failed). errno = %d\n",ip,port,errno);
  120. // printf("FD_ISSET(sock_fd, &rfds): %d\n FD_ISSET(sock_fd, &wfds): %d\n", FD_ISSET(sock_fd, &rfds) , FD_ISSET(sock_fd, &wfds));
  121. return false;
  122. }
  123. }
  124. else
  125. {
  126. printf("connect %s : %d finished(failed).",ip,port);
  127. return false;
  128. }
  129. }
  130. }
  131. else //连接正常
  132. {
  133. set_block(true); //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
  134. printf("connect %s : %d finished(success).\n",ip,port);
  135. return true;
  136. }
  137. }
  138. bool Communication_tcp::communication_tcp_connect(int sec)
  139. {
  140. if (m_socket <= 0) return false;
  141. sockaddr_in saddr; //设置连接对象的结构体
  142. saddr.sin_family = AF_INET;
  143. saddr.sin_port = htons(m_server_port);
  144. saddr.sin_addr.s_addr = inet_addr(m_server_ip); //字符串转整型
  145. set_block(false); //将socket改成非阻塞模式,此时它会立即返回 所以通过fd_set
  146. fd_set rfds, wfds; //文件句柄数组,在这个数组中,存放当前每个文件句柄的状态
  147. if (connect(m_socket, (sockaddr*)&saddr, sizeof(saddr)) != 0) //此时connect马上返回,状态为未成功连接
  148. {
  149. FD_ZERO(&rfds); //首先把文件句柄的数组置空
  150. FD_ZERO(&wfds);
  151. FD_SET(m_socket, &rfds); //把sock的网络句柄加入到该句柄数组中
  152. FD_SET(m_socket, &wfds);
  153. timeval tm; //超时参数的结构体
  154. tm.tv_sec = sec;
  155. tm.tv_usec = 0;
  156. int selres = select(m_socket + 1, &rfds, &wfds, NULL, &tm); //(阻塞函数)(监听的文件句柄的最大值加1,可读序列文件列表,可写的序列文件列表,错误处理,超时)使用select监听文件序列set是否有可读可写,这里监听set数组(里面只有sock),只要其中的句柄有一个变得可写(在这里是sock连接成功了以后就会变得可写,就返回),就返回
  157. switch (selres)
  158. {
  159. case -1:
  160. printf("select error\n");
  161. return false;
  162. case 0:
  163. printf("select time out\n");
  164. return false;
  165. default:
  166. if (FD_ISSET(m_socket, &rfds) || FD_ISSET(m_socket, &wfds))
  167. {
  168. connect(m_socket, (sockaddr*)&saddr, sizeof(saddr)); //再次连接一次进行确认
  169. int err = errno;
  170. if (err == EISCONN||err == EINPROGRESS) //已经连接到该套接字 或 套接字为非阻塞套接字,且连接请求没有立即完成
  171. {
  172. //printf("connect %s : %d finished(success).\n",m_server_ip,m_server_port);
  173. set_block(true); //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
  174. return true;
  175. }
  176. else
  177. {
  178. printf("connect %s : %d finished(failed). errno = %d\n",m_server_ip,m_server_port,errno);
  179. // printf("FD_ISSET(sock_fd, &rfds): %d\n FD_ISSET(sock_fd, &wfds): %d\n", FD_ISSET(sock_fd, &rfds) , FD_ISSET(sock_fd, &wfds));
  180. return false;
  181. }
  182. }
  183. else
  184. {
  185. printf("connect %s : %d finished(failed).",m_server_ip,m_server_port);
  186. return false;
  187. }
  188. }
  189. }
  190. else //连接正常
  191. {
  192. printf("connect %s : %d finished(success).\n",m_server_ip,m_server_port);
  193. set_block(true); //成功之后重新把sock改成阻塞模式,以便后面发送/接收数据
  194. return true;
  195. }
  196. }
  197. Communication_tcp Communication_tcp::Accept() //返回Communication_tcp对象,接收连接
  198. {
  199. Communication_tcp tcp; //先定义一个Communication_tcp对象,一会返回它
  200. sockaddr_in caddr;
  201. socklen_t len = sizeof(caddr);
  202. tcp.m_socket = accept(m_socket, (sockaddr*)&caddr, &len); //(阻塞)接收连接 ,会创建一个新的socket,一般扔到一个单独线程与这个客户端进行单独通信,之前的sock只用来建立连接
  203. if (tcp.m_socket <= 0)
  204. return tcp; //出错
  205. printf("accept client socket %d\n", tcp.m_socket);
  206. char *ip = inet_ntoa(caddr.sin_addr); //解析出IP地址 ,转换到字符串
  207. strcpy(tcp.m_client_ip, ip);
  208. tcp.m_client_port = ntohs(caddr.sin_port); //解析出端口,转换成主机字节序
  209. printf("client ip is %s,port is %d\n", tcp.m_client_ip, tcp.m_client_port); //打印ip和端口
  210. return tcp;
  211. }
  212. void Communication_tcp::communication_tcp_uninit() //关闭连接
  213. {
  214. if (m_socket <= 0)
  215. {
  216. printf("socket %d error \n", m_socket); //打印ip和端口
  217. return; //socket出错
  218. }
  219. close(m_socket);
  220. }
  221. int Communication_tcp::communication_tcp_recv(char *buf, int size) //接收数据
  222. {
  223. return recv(m_socket, buf, size, 0);
  224. }
  225. int Communication_tcp::communication_tcp_send(const char *buf, int size) //发送数据
  226. {
  227. int sendedSize = 0; //已发送成功的长度
  228. while (sendedSize != size) //若没发送完成,则从断点开始继续发送 直到完成
  229. {
  230. try
  231. {
  232. int len = send(m_socket, buf + sendedSize, size - sendedSize, 0);
  233. if (len <= 0)
  234. break;
  235. sendedSize += len;
  236. }
  237. catch (char *str)
  238. {
  239. std::cout << " 断线---" << str<< std::endl;
  240. break;
  241. }
  242. }
  243. return sendedSize;
  244. }
  245. int Communication_tcp::set_recv_timeout(int sec) //设置tcp接收超时
  246. {
  247. struct timeval tcp_rev_time;
  248. tcp_rev_time.tv_sec = sec;
  249. tcp_rev_time.tv_usec = 0;
  250. if (setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tcp_rev_time, sizeof(tcp_rev_time))<0)
  251. {
  252. printf("set tcp receive failed.\n");
  253. return -1;
  254. }
  255. printf("set tcp recv timeout success. %d seconds.\n", sec);
  256. return 0;
  257. }
  258. int Communication_tcp::set_send_timeout(int sec) //设置tcp发送超时
  259. {
  260. struct timeval tcp_send_time;
  261. tcp_send_time.tv_sec = sec;
  262. tcp_send_time.tv_usec = 0;
  263. if (setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tcp_send_time, sizeof(tcp_send_time))<0)
  264. {
  265. printf("set tcp send failed.\n");
  266. return -1;
  267. }
  268. printf("set tcp recv timeout success. %d seconds.\n", sec);
  269. return 0;
  270. }
  271. bool Communication_tcp::is_connected()
  272. {
  273. struct tcp_info info;
  274. int len=sizeof(info);
  275. getsockopt(m_socket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
  276. if((info.tcpi_state==TCP_ESTABLISHED))
  277. {
  278. return true;
  279. }
  280. return false;
  281. }