102.tcp实现多线程连接与群聊
- 协议之间的关系
- 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 }