一、​​服务器​​部分代码

头文件:server.h

  1. #include <winsock2.h>
  2. #include <stdio.h>
  3. #include <windef.h>

  4. #ifndef TCP_SERVER_H
  5. #define TCP_SERVER_H

  6. #pragma warning(disable : 4996)


  7. #define MAX_CLIENT (100)
  8. #define LISTEN_PORT (60000)
  9. #define CLIENT_TIMEOUT (15 * 60 * 60) /*客户端中断15分钟则​​服务器​​自动掉线*/
  10. #define MAX_TCP_PACKET_SIZE (1492)/*tcp最大数据包字节数*/

  11. typedef struct __client_item
  12. {
  13. SOCKET client;
  14. sockaddr_in form;
  15. unsigned int id;
  16. DWORD dwThreadId;
  17. HANDLE hThread;
  18. }_client_item;

  19. typedef struct __client_list
  20. {
  21. DWORD client_cnt;
  22. _client_item client_item[MAX_CLIENT];
  23. }_client_list;



  24. DWORD WINAPI serve_client_thread_func( LPVOID param_ptr );

  25. #endif/*TCP_SERVER_H*/

源文件:server.cpp

  1. #include "server.h"
  2. #include <sys\stat.h>
  3. #include <time.h>


  4. _client_list client_list;

  5. CRITICAL_SECTION cs_serve_main;
  6. CRITICAL_SECTION cs_serve_thread;


  7. _client_list *get_client_list(void)
  8. {
  9. return &client_list;
  10. }

  11. void client_list_cnt_inc(void)
  12. {
  13. _client_list *client_list_ptr = get_client_list();

  14. client_list_ptr->client_cnt++;
  15. }

  16. void client_list_cnt_dec(void)
  17. {
  18. _client_list *client_list_ptr = get_client_list();

  19. client_list_ptr->client_cnt--;
  20. }

  21. unsigned int get_free_client_index(void)
  22. {
  23. int i = 0;
  24. unsigned int id = 0;
  25. _client_list *client_list_ptr = get_client_list();

  26. for(i = 0; i < MAX_CLIENT; i++)
  27. {
  28. id = client_list_ptr->client_item[i].id;
  29. if( UINT_MAX == id )
  30. {
  31. break;
  32. }
  33. }

  34. return i;
  35. }

  36. _client_item *get_client_item(unsigned int index)
  37. {
  38. _client_list *client_list_ptr = get_client_list();

  39. if( index >= MAX_CLIENT )
  40. {
  41. return NULL;
  42. }

  43. return &client_list_ptr->client_item[index];
  44. }

  45. void set_client_item(unsigned int index, _client_item *client_item_ptr)
  46. {
  47. _client_list *client_list_ptr = get_client_list();

  48. if( index >= MAX_CLIENT )
  49. {
  50. return;
  51. }

  52. client_list_ptr->client_item[index] = *client_item_ptr;
  53. }


  54. SOCKET g_sk_server;

  55. int main()
  56. {
  57. SOCKET server;
  58. WSADATA wsaData;
  59. sockaddr_in local;
  60. int nRet = 0;
  61. int sockaddr_in_sizeof = 0;
  62. unsigned int index = 0;
  63. _client_item *client_item_ptr = NULL;
  64. _client_item client_item = {0};
  65. _client_list *client_list_ptr = NULL;
  66. SOCKET client;
  67. sockaddr_in from;
  68. DWORD dwThreadId;
  69. HANDLE hThread;

  70. nRet = WSAStartup(0x101, &wsaData);

  71. if( 0 != nRet )
  72. {
  73. return 0;
  74. }


  75. // 现在我们来为sockaddr_in结构赋值。
  76. local.sin_family = AF_INET; // 地址族
  77. local.sin_addr.s_addr = INADDR_ANY; // 网际IP地址
  78. local.sin_port = htons(LISTEN_PORT); // 使用的端口

  79. // 由socket函数创建我们的SOCKET。
  80. server = socket(AF_INET, SOCK_STREAM, 0);
  81. //server = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL, 0, WSA_FLAG_OVERLAPPED);

  82. // 如果socket()函数失败,我们就退出。
  83. if( server == INVALID_SOCKET )
  84. {
  85. return 0;
  86. }

  87. // bind将我们刚创建的套接字和sockaddr_in结构联系起来。
  88. // 它主要使用本地地址及一个特定的端口来连接套接字。
  89. // 如果它返回非零值,就表示出现错误。
  90. nRet = bind(server, (sockaddr*)&local, sizeof(local));
  91. if( 0 != nRet )
  92. {
  93. return 0;
  94. }

  95. // listen命令套接字监听来自客户端的连接。
  96. // 第二个参数是最大连接数。
  97. nRet = listen(server, MAX_CLIENT);
  98. if( 0 != nRet )
  99. {
  100. return 0;
  101. }

  102. g_sk_server = server;

  103. // 我们需要一些变量来保存客户端的套接字,因此我们在此声明之。
  104. InitializeCriticalSection(&cs_serve_main);/*初始化临界区*/
  105. InitializeCriticalSection(&cs_serve_thread);/*初始化临界区*/
  106. client_list_ptr = get_client_list();
  107. memset(client_list_ptr, 0xff, sizeof(_client_list));
  108. memset(&client, 0, sizeof(SOCKET));
  109. memset(&from, 0, sizeof(sockaddr_in));

  110. sockaddr_in_sizeof = sizeof(sockaddr_in);

  111. while( TRUE )
  112. {
  113. index = get_free_client_index();

  114. if( index >= MAX_CLIENT )
  115. {/*已经达到最大连接数*/
  116. continue;
  117. }

  118. client_item_ptr = get_client_item(index);

  119. client_item_ptr->client = accept(server,
  120. (struct sockaddr*)&client_item_ptr->form, &sockaddr_in_sizeof);

  121. if( INVALID_SOCKET == client_item_ptr->client )
  122. {/*连接错误*/
  123. Sleep(500);
  124. continue;
  125. }else
  126. {
  127. printf("Connection from %s\n", inet_ntoa(client_item_ptr->form.sin_addr) );
  128. }

  129. /* 多线程开始*/
  130. EnterCriticalSection(&cs_serve_main);/*进入临界区*/
  131. hThread = CreateThread(
  132. NULL, // no security attributes
  133. 0, // use default stack size
  134. serve_client_thread_func, // thread function
  135. client_item_ptr, // argument to thread function
  136. CREATE_SUSPENDED, // use default creation flags
  137. &dwThreadId); // returns the thread identifier

  138. if( NULL != hThread )
  139. {
  140. client_item_ptr->hThread = hThread;
  141. client_item_ptr->dwThreadId = dwThreadId;
  142. client_item_ptr->id = index;
  143. }
  144. client_list_cnt_inc();

  145. LeaveCriticalSection(&cs_serve_main);/*离开临界区*/

  146. if( NULL != client_item_ptr->hThread )
  147. {
  148. ResumeThread(client_item_ptr->hThread);
  149. }

  150. client_item_ptr = NULL;
  151. /*end 多线程开始*/
  152. }

  153. //关闭套接字,并释放套接字描述符。
  154. closesocket(server);
  155. WSACleanup();

  156. DeleteCriticalSection(&cs_serve_thread);/*删除临界区*/
  157. DeleteCriticalSection(&cs_serve_main);/*删除临界区*/

  158. return 0;
  159. }

  160. void serve_client_thread_clear(SOCKET sock_client,
  161. sockaddr_in *sockaddr_client_ptr,
  162. UINT32 client_id,
  163. char *msg,
  164. _client_item *client_item_ptr
  165. )
  166. {
  167. int msg_len = strlen(msg);

  168. if( msg_len > 0 )
  169. {
  170. send(sock_client, msg, msg_len + 1, 0);
  171. }

  172. closesocket(sock_client);/*关闭连接*/

  173. client_list_cnt_dec();

  174. memset(client_item_ptr, 0, sizeof(_client_item));
  175. set_client_item(client_id, client_item_ptr);
  176. }

  177. DWORD WINAPI serve_client_thread_func(LPVOID param_ptr)
  178. {
  179. char buf[MAX_TCP_PACKET_SIZE];
  180. _client_item *client_item_ptr = (_client_item *)param_ptr;
  181. SOCKET sock_client = 0;
  182. sockaddr_in sockaddr_client = {0};
  183. unsigned int client_id = 0;
  184. _client_list *client_list_ptr = get_client_list();
  185. int recv_size = 0;
  186. time_t sock_free_cur_time = 0;
  187. time_t sock_free_kill_time = 0;

  188. int n_ret = 0;
  189. fd_set fdread;
  190. timeval tv;

  191. while( NULL == client_item_ptr->hThread );

  192. sock_client = client_item_ptr->client;
  193. sockaddr_client = client_item_ptr->form;
  194. client_id = client_item_ptr->id;

  195. sprintf(buf, "%s:%d you id=%d\n", inet_ntoa(sockaddr_client.sin_addr),
  196. sockaddr_client.sin_port, client_id);

  197. send(sock_client, buf, strlen(buf) + 1, 0);

  198. do
  199. {
  200. /*这部分代码在此处必须每次都执行*/
  201. FD_ZERO(&fdread);//初始化fd_set
  202. FD_SET(sock_client, &fdread);//分配套接字句柄到相应的fd_set
  203. tv.tv_sec = 1;//这里我们打算让select等待1s后返回,避免被锁死,也避免马上返回
  204. tv.tv_usec = 0;
  205. /*end 这部分代码在此处必须每次都执行*/

  206. select(0, &fdread, NULL, NULL, &tv);
  207. n_ret = FD_ISSET(sock_client, &fdread);
  208. if( 0 == n_ret )/*没有数据*/
  209. {
  210. continue;
  211. }

  212. recv_size = recv(sock_client, buf, sizeof(buf), 0);
  213. if( recv_size <= 0)
  214. {/*socket 错误,断开连接并退出*/
  215. /*超时,线程退出*/
  216. printf("client time out ip=%s:port=%d:id=%d\n",
  217. inet_ntoa(sockaddr_client.sin_addr), sockaddr_client.sin_port, client_id);

  218. EnterCriticalSection(&cs_serve_thread);/*进入临界区*/

  219. memset(buf, 0, sizeof(buf));
  220. serve_client_thread_clear(sock_client, &sockaddr_client, client_id, buf, client_item_ptr);

  221. LeaveCriticalSection(&cs_serve_thread);/*离开临界区*/

  222. ExitThread(0);/*退出线程*/
  223. }
  224. sock_free_cur_time = 0;

  225. if( ('q' == buf[0]) && (0 == buf[1]) )
  226. {/*客户端主动退出*/
  227. sprintf(buf, "client exit server ip=%s:port=%d:id=%d\n",
  228. inet_ntoa(sockaddr_client.sin_addr), sockaddr_client.sin_port, client_id);

  229. EnterCriticalSection(&cs_serve_thread);/*进入临界区*/

  230. serve_client_thread_clear(sock_client, &sockaddr_client, client_id, buf, client_item_ptr);

  231. LeaveCriticalSection(&cs_serve_thread);/*离开临界区*/

  232. ExitThread(0);/*退出线程*/
  233. }
  234. printf("ip=%s:port=%d:id=%d say:%s\n", inet_ntoa(sockaddr_client.sin_addr),
  235. sockaddr_client.sin_port, client_id, buf);
  236. }while( TRUE );

  237. return 0;
  238. }

2、客户端代码 头文件:client.h

  1. #include <winsock2.h>
  2. #include <stdio.h>
  3. #include <windef.h>


  4. #ifndef TCP_CLIENT_H
  5. #define TCP_CLIENT_H

  6. #pragma warning(disable : 4996)


  7. #define TCP_SERVER_ADDR "192.168.0.2"
  8. #define TCP_SERVER_PORT (60000)
  9. #define MAX_TCP_PACKET_SIZE (1492)/*tcp最大数据包字节数*/

  10. #endif/*end TCP_CLIENT_H*/

源文件:client.cpp

  1. #include "client.h"

  2. int main()
  3. {
  4. int nRet = 0;
  5. WORD wVersionRequested;//版本号
  6. WSADATA wsaData;
  7. SOCKET sock;
  8. SOCKADDR_IN server_addr;
  9. char buf[MAX_TCP_PACKET_SIZE];
  10. int recv_size = 0;

  11. wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
  12. nRet = WSAStartup(wVersionRequested, &wsaData);
  13. if( nRet )
  14. {
  15. return nRet;
  16. }

  17. if (LOBYTE(wsaData.wVersion) != 1 ||
  18. HIBYTE(wsaData.wVersion) != 1)
  19. {
  20. return -1;
  21. }//判断高低字节是不是1,如果不是1.1的版本则退出

  22. sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  23. if( INVALID_SOCKET == sock )
  24. {
  25. return -1;
  26. }

  27. server_addr.sin_addr.S_un.S_addr = inet_addr(TCP_SERVER_ADDR);
  28. server_addr.sin_family = AF_INET;
  29. server_addr.sin_port = htons(TCP_SERVER_PORT);
  30. nRet = connect(sock, (SOCKADDR*)&server_addr, sizeof(SOCKADDR) );
  31. if( 0 != nRet )
  32. {
  33. return nRet;
  34. }

  35. recv(sock, buf, sizeof(buf), 0);
  36. printf(buf);

  37. do
  38. {
  39. memset(buf, 0, sizeof(buf));
  40. printf("please write some thing:");
  41. gets(buf);

  42. send(sock, buf, strlen(buf) + 1, 0);
  43. if( ('q'== buf[0]) && (0 == buf[1]) )
  44. {
  45. recv_size = recv(sock, buf, sizeof(buf), 0);
  46. printf(buf);
  47. break;
  48. }
  49. }while( TRUE );

  50. closesocket(sock);
  51. WSACleanup();//必须调用这个函数清除参数

  52. return 0;
  53. }