一、设计工具
1.ide: VS2010旗舰版(学生版应该也可以)
2.编程语言: C++
二、效果展示
1.服务器效果
服务器只转发消息不参到信息交流中
2.客户端效果
客户端输入用户名后默认进入群聊模式,输入“获取用户列表”可以获取在线用户用户名列表,如果需要私聊某一个用户需要输入“私聊+用户名”可进入私聊模式,输入“退出私聊”可退出私聊重新进入群聊模式。
基本群聊展示
获取用户列表展示
私聊效果展示
三、代码讲解
1.C++使用TCP协议基本结构
ps:我们在使用tcp协议是不需要完全了解这部分代码的作用,只需要了解怎么初始化和使用它就行
1.确定协议版本信息
2.创建socket
3.确定服务器地址族
4.连接服务器
5.使用后关闭scoket和清理协议版本信息,减少系统占用
2.客户端程序
(1)连接到客户端时使用用户名设定模块,将输入的用户名发送到服务器存储
(2)通信函数,用于在多线程函数中使用
(3)收发消息实现模块,如果收到消息,使用标准输出b和输出空格遮挡通信函数输出的基础内容,再重新打印一次使得控制台程序收发消息美观
(4)客户端整体代码如下
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127#include<stdio.h> #include<WinSock.h> #include<Windows.h> #pragma comment(lib,"ws2_32.lib") //创建socket数组 SOCKET serverSocket; //全局变量 char name[256] = "设定用户名"; //函数声明 static int getNumber(const char *s);//中英文混合用户名长度检测函数 void findIP(char *ip, int size);//读取本机一个ip void yonghu() { //5通信 char buff[256]; while(1) { printf("%s:>",name); scanf("%s",buff); send(serverSocket,buff,strlen(buff),NULL); } } int main() { printf("n**********************欢迎使用 210745班 小马的 tcp聊天室*************************"); printf("n**************默认为群聊模式,输入“获取用户列表”可获取在线用户列表*************"); printf("n*****输入“私聊+对方用户名”可进入私聊模式,输入“退出私聊”可以恢复群聊模式*****n"); char ip[20] = {0}; findIP(ip, sizeof(ip));//读取本机ip //initgraph(300,300,1); //图形库函数 //1 确定协议版本信息 WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("确定版本协议信息错误:%d",GetLastError()); return -1; } printf("确定版本协议信息成功n"); //2 创建socket serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if( SOCKET_ERROR == serverSocket) { WSACleanup(); printf("创建socket错误:%d",GetLastError()); return -1; } printf("创建socket成功n"); //3 确定服务器协议地址族 SOCKADDR_IN sAddr = { 0 }; sAddr.sin_family = AF_INET; sAddr.sin_addr.S_un.S_addr = inet_addr(ip); sAddr.sin_port = htons(9527);//一般的pc机 65536 //4 连接服务器 int r = connect(serverSocket,(sockaddr*)&sAddr,sizeof sAddr); if(-1 == r) { closesocket(serverSocket); WSACleanup(); printf("连接服务器错误:%dn",GetLastError()); } printf("连接服务器成功n"); //用户名设定模块 char buff[256]; sprintf(buff,"%s",name); send(serverSocket,buff,strlen(buff),NULL); memset(name,0,sizeof(name)); printf("n请输入用户名:"); scanf("%s",name); sprintf(buff,"%s",name); send(serverSocket,buff,strlen(buff),NULL); //同时要接受服务器发来的数据并现实 CreateThread(NULL,NULL, (LPTHREAD_START_ROUTINE)yonghu, NULL,NULL,NULL); //接受服务器发来的消息并显示 char recvBuff[256]; int n = 0; while(1) { r = recv(serverSocket, recvBuff, 255, NULL); recvBuff[r] = 0; if(r>0) { for(r=0; r<=getNumber(name)+2; r++) { printf("b"); } for(r=0; r<=getNumber(name)+2; r++) { printf(" "); } for(r=0; r<=getNumber(name)+2; r++) { printf("b"); } printf("%sn",recvBuff); printf("%s:>",name); } out:; } //6关闭socket closesocket(serverSocket); //7清理协议版本信息 WSACleanup(); return 0; } static int getNumber(const char *s) { int i = 0, j = 0; while (s[i]) { if ((s[i] & 0xc0) != 0x80) j++; i++; } return j; } void findIP(char *ip, int size) { WORD v = MAKEWORD(1, 1); WSADATA wsaData; WSAStartup(v, &wsaData); // 加载套接字库 struct hostent *phostinfo = gethostbyname(""); char *p = inet_ntoa (* ((struct in_addr *)(*phostinfo->h_addr_list)) ); strncpy(ip, p, size - 1); ip[size - 1] = ''; WSACleanup( ); }
3.服务器程序
(1)除accept函数处理部分tcp协议初始化步骤与客户端相同,这里不再赘述
(2)将accept函数放在一个for循环中接受多个客户端连接,NUM是一个宏定义的参数,可以设置最大用户接入数量
(3)设定用户名模块。使用字符串处理函数strcmp识别特殊字符,存储客户端用户名到一个指针数组,strcat可以将用户名拼接到用户列表中
(4)私聊模块。服务器通过识别特殊字符开启私聊模式,因为我设计程序时用户名指针数组跟socket的下标是对应的,利用这个特性可以用循环识别客户端需要私聊的用户,发送给对于socket[]数组中的一个就实现了私聊,输入退出私聊时服务器也可以识别,使用goto out:跳出这个模块。
(5)群聊模块。在群聊模式下,使用for循环将收到的客户端消息广播到所有客户端
(6)服务器端整体代码如下
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194#include<stdio.h> #include<WinSock.h> #include<Windows.h> #pragma comment(lib,"ws2_32.lib") //宏定义 #define NUM 1024 //函数声明 static int getNumber(const char *s);//中英文混合用户名长度检测函数 void findIP(char *ip, int size);//读取本机一个ip //全局变量 char *name[1023];//1024个用户名 int clientCount; char namelist[512] = "*******用户列表*******n"; //创建socket数组 SOCKET clientSocket[NUM];//设置最多1024客户端接入 void tongxin(int idx) { //7 通信 char buff[256]; char mid[256]; int r; int i; char temp[256]; char siliao[256]; char num[256]; while(1) { r = recv(clientSocket[idx],buff,255,NULL); if (r>0) { buff[r] = 0;//添加结束符号 if(strcmp(buff,"设定用户名") == 0) {//用户名设定模块 memset(buff,0,256); recv(clientSocket[idx],buff,255,NULL); strcpy(mid,buff); name[idx] = mid; sprintf(name[idx],"%s",buff); //更新用户列表 sprintf(temp,"%s",buff); strcat(namelist,""); strcat(namelist,name[idx]); strcat(namelist,"n"); goto out; } if(strcmp(buff,"获取用户列表") == 0) { memset(temp,0,256); strcpy(temp,namelist); send(clientSocket[idx],temp,strlen(temp),NULL); goto out4; } for(i=0;i<=1023;i++) { sprintf(siliao,"私聊%s",name[i]); if(strcmp(buff,siliao) == 0) { memset(temp,0,256);//清空数组 sprintf(temp,"服务器已准备好向%s发送私聊消息n输入“退出私聊”可以继续群聊n",name[i]); send(clientSocket[idx],temp,strlen(temp),NULL); memset(temp,0,256);//清空数组 sprintf(temp,"%s将对您发送私聊消息n",name[idx]); send(clientSocket[i],temp,strlen(temp),NULL); while(1) { memset(buff,0,256);//清空数组 recv(clientSocket[idx],buff,255,NULL); printf("%s:>%sn",name[i],buff); if(strcmp(buff,"退出私聊") == 0) { memset(temp,0,256); sprintf(temp,"已退出私聊模式,即将进入群聊n",name); send(clientSocket[idx],temp,strlen(temp),NULL); goto out2; } memset(temp,0,256);//清空数组 sprintf(temp,"%s:>%s",name[idx],buff); send(clientSocket[i],temp,strlen(temp),NULL); } } } //printf(">>:%sn",buff); memset(temp,0,256);//清空数组 sprintf(temp,"%s:>%s",name[idx],buff); for(int i = 0;i < clientCount;i++) {//广播 if( i == idx ) continue; //不广播给自己 send(clientSocket[i],temp,strlen(temp),NULL); } } out:; out2:; out3:;out4:; } } int main() { clientCount = 0; char ip[20] = {0}; findIP(ip, sizeof(ip));//读取本机ip //1 确定协议版本信息 WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("确定版本协议信息错误:%d",GetLastError()); return -1; } printf("确定版本协议信息成功n"); //2 创建socket SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if( SOCKET_ERROR == serverSocket) { //9清理协议版本信息 WSACleanup(); printf("创建socket错误:%d",GetLastError()); return -1; } printf("创建socket成功n"); //3 确定服务器协议地址族 SOCKADDR_IN sAddr = { 0 }; sAddr.sin_family = AF_INET; sAddr.sin_addr.S_un.S_addr = inet_addr(ip); sAddr.sin_port = htons(9527);//一般的pc机 65536 //4 绑定 int r = bind(serverSocket,(sockaddr*)&sAddr,sizeof(sAddr)); if(-1 == r) { //8关闭socket closesocket(serverSocket); //9清理协议版本信息 WSACleanup(); printf("绑定错误:%dn",GetLastError()); } printf("绑定成功n"); //5 监听 r = listen(serverSocket,10); if(-1 == r) { //8关闭socket closesocket(serverSocket); //9清理协议版本信息 WSACleanup(); printf("监听错误:%dn",GetLastError()); } printf("监听成功n"); //6 接受连接 for(int i = 0; i<NUM; i++) { clientSocket[i] = accept(serverSocket,NULL,NULL); if( SOCKET_ERROR == clientSocket[i]) { //8关闭socket closesocket(serverSocket); //9清理协议版本信息 WSACleanup(); printf("网络崩了%dn",GetLastError()); return -1; } printf("客户端连接上服务器了!n"); clientCount++; //7通信 //并发 两个循环同时进行 CreateThread(NULL,NULL, (LPTHREAD_START_ROUTINE)tongxin, (LPVOID)i, NULL,NULL); } //8关闭socket closesocket(serverSocket); //9清理协议版本信息 WSACleanup(); return 0; } static int getNumber(const char *s) { int i = 0, j = 0; while (s[i]) { if ((s[i] & 0xc0) != 0x80) j++; i++; } return j; } void findIP(char *ip, int size) { WORD v = MAKEWORD(1, 1); WSADATA wsaData; WSAStartup(v, &wsaData); // 加载套接字库 struct hostent *phostinfo = gethostbyname(""); char *p = inet_ntoa (* ((struct in_addr *)(*phostinfo->h_addr_list)) ); strncpy(ip, p, size - 1); ip[size - 1] = ''; WSACleanup( ); }
看到这里的大佬们,代码我纯手打,可能存在bug,欢迎各位指正,也希望你们能为我点一个大大的赞!将这份知识传递下去!
最后
以上就是丰富外套最近收集整理的关于基于TCP协议的聊天室详细教学(C++)一、设计工具二、效果展示 三、代码讲解1.C++使用TCP协议基本结构的全部内容,更多相关基于TCP协议内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复