1. 程式人生 > >102.tcp實現多線程連接與群聊

102.tcp實現多線程連接與群聊

多線程 scan accept hand 客戶端 ever 創建線程 自己的 簡單的

  • 協議之間的關系

技術分享圖片

  • socket在哪

技術分享圖片

  • socket是什麽

 Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

  門面模式,用自己的話說,就是系統對外界提供單一的接口,外部不需要了解內部的實現。

  • socket編程的基本流程

技術分享圖片

tcp通信實現多線程連接與群聊

服務器端

  • 定義端口以及本地ip地址
    1 #define port 9876
    2 #define ip_addr "192.168.1.102"

  • 創建事件以及互斥量

    1 HANDLE event;//事件
    2 HANDLE mutex = NULL;
    1 event = CreateEvent(NULL, TRUE, FALSE, NULL);//第二個參數TRUE表示手動復位
    2     mutex = CreateMutex(NULL, FALSE, NULL);//互相排斥

  • 接收連接

     1 //接受連接
     2 void recv_connect(void *p)
     3 {
     4     WSADATA WSA;//對比版本
     5 
     6     SOCKET client,sever;//客戶端
     7 
     8     //本地地址信息,以及連接的客戶端地址信息
    9 struct sockaddr_in localeaddr,clientaddr; 10 int addrlength = 0; 11 HANDLE hthread1 = NULL;//線程句柄 12 HANDLE hthread2 = NULL; 13 HANDLE hthread3 = NULL; 14 int Ret = 0; 15 char senbuf[256] = { 0 }; 16 17 //對比版本 18 if (WSAStartup(MAKEWORD(2, 2), &WSA) != 0) 19 {
    20 puts("版本不一致,通信失敗"); 21 system("pause"); 22 return; 23 } 24 //創建通信 25 sever = socket(AF_INET, SOCK_STREAM, 0); 26 if (sever == INVALID_SOCKET) 27 { 28 puts("服務器創建失敗"); 29 system("pause"); 30 return; 31 } 32 //設置服務器結構體信息 33 localeaddr.sin_family = AF_INET; 34 localeaddr.sin_addr.s_addr = inet_addr(ip_addr); 35 localeaddr.sin_port = htons(port); 36 memset(localeaddr.sin_zero, 0x00, 8);//清零 37 //與socket綁定 38 Ret = bind(sever, (struct sockaddr*)&localeaddr, sizeof(localeaddr)); 39 if (Ret != 0) 40 { 41 puts("綁定失敗"); 42 system("pause"); 43 return; 44 } 45 Ret = listen(sever, 5); 46 if (Ret != 0) 47 { 48 puts("監聽失敗"); 49 system("pause"); 50 return; 51 } 52 puts("服務器啟動"); 53 54 while (1) 55 { 56 addrlength = sizeof(clientaddr);//獲取長度 57 //接受客戶端連接,信息存放在clientaddr中 58 client = accept(sever, (struct sockaddr*) &clientaddr, &addrlength); 59 if (client == INVALID_SOCKET) 60 { 61 puts("接收失敗"); 62 system("pause"); 63 return; 64 } 65 printf("\n客戶端連接%s 端口號:%d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port); 66 //創建寫的線程 67 hthread3 = CreateThread(NULL, 0, clientthreadwrite, (void*)client, 0, NULL); 68 } 69 70 //關閉socket 71 closesocket(sever); 72 closesocket(client); 73 WSACleanup(); 74 }

  • 向客戶端發送消息的多線程函數

     1 //創建線程向客戶端發送消息
     2 DWORD WINAPI clientthreadwrite(void *p)
     3 {
     4     SOCKET client = (SOCKET)p;//數指針類型轉換
     5     int Ret = 0;
     6 
     7     while (1)
     8     {
     9         WaitForSingleObject(event, INFINITE);//等待事件
    10         WaitForSingleObject(mutex, INFINITE);
    11         
    12         if (strlen(sendbuf) != 0)
    13         {
    14             //發送信息
    15             Ret = send(client, sendbuf, strlen(sendbuf), 0);
    16         }
    17         ReleaseMutex(mutex);
    18         ResetEvent(event);//手動復位
    19     }
    20 }

  • 從客戶端接收消息的多線程函數

     1 DWORD WINAPI clientthreadread(void *p)
     2 {
     3     //數指針類型轉換
     4     SOCKET client = (SOCKET)p;
     5     int Ret = 0;
     6     char receivebuf[256];
     7 
     8     while (1)
     9     {
    10         //清零
    11         memset(receivebuf, 0, 256);
    12         //讀取
    13         Ret = recv(client, receivebuf, 256, 0);
    14         if (Ret == SOCKET_ERROR)
    15         {
    16             puts("客戶端send失敗");
    17             break;
    18         }
    19         printf("\n收到%s,", receivebuf);    
    20         //進入臨界區
    21         WaitForSingleObject(mutex, INFINITE);
    22         memset(sendbuf, 0, 256);
    23         //全局變量,鎖定
    24         strcpy(sendbuf, receivebuf);
    25         ReleaseMutex(mutex);
    26         //通知
    27         SetEvent(event);
    28     }
    29     return 0;
    30 }

      
  • main函數
     1 void main()
     2 {
     3     event = CreateEvent(NULL, TRUE, FALSE, NULL);//第二個參數TRUE表示手動復位
     4     mutex = CreateMutex(NULL, FALSE, NULL);//互相排斥
     5 
     6     _beginthread(recv_connect, 0, NULL);
     7 
     8     Sleep(100);
     9     while (1)
    10     {
    11         printf("請輸入要發送的信息:");
    12         scanf("%s", sendbuf);
    13         SetEvent(event);
    14     }
    15     system("pause");
    16 }

客戶端

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include <winsock.h>
 5 #pragma comment(lib,"ws2_32.lib")
 6 
 7 //定義端口號
 8 #define port 9876
 9 //要連接的ip地址
10 #define ip_addr "192.168.1.102"
11 
12 
13 void main()
14 {
15     //對比版本
16     WSADATA WSA;
17     //客戶端套接字
18     SOCKET client;
19     //服務器信息
20     struct sockaddr_in severaddr;
21     //線程句柄
22     HANDLE hthread = NULL;
23     //保存連接信息
24     int Ret = 0;
25     char senbuf[256] = { 0 };
26 
27     if (WSAStartup(MAKEWORD(2,2),&WSA)!=0)
28     {
29         puts("版本不一致,通信失敗");
30         system("pause");
31         return;
32     }
33     //創建socket
34     client = socket(AF_INET, SOCK_STREAM, 0);
35     if (client == INVALID_SOCKET)
36     {
37         puts("客戶端創建失敗");
38         system("pause");
39 
40     }
41     //設置服務器信息
42     severaddr.sin_family = AF_INET;
43     //設置地址
44     severaddr.sin_addr.s_addr = inet_addr(ip_addr);
45     //端口
46     severaddr.sin_port = htons(port);
47     //清空
48     memset(severaddr.sin_zero, 0x00, 8);
49 
50     //連接
51     Ret = connect(client, (struct sockaddr*) &severaddr, sizeof(severaddr));
52     if (Ret!=0)
53     {
54         puts("客戶端鏈接失敗");
55         system("pause");
56     }
57     while (1)
58     {
59         //printf("請輸入向服務器發送的消息:");
60         //scanf("%s", senbuf);//輸入
61         //Ret = send(client, senbuf, strlen(senbuf), 0);//發送
62         //if (Ret==SOCKET_ERROR)
63         //{
64         //    puts("客戶端send失敗");
65         //    system("pause");
66         //}
67         char receivebuf[256];
68         memset(receivebuf, 0, 256);//清零
69         Ret = recv(client, receivebuf, 256, 0);
70         printf("收到客戶端發送的消息:%s\n", receivebuf);
71     }
72 
73     closesocket(client);
74     WSACleanup();
75 }

102.tcp實現多線程連接與群聊