一、设计工具
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)客户端整体代码如下
#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("连接服务器错误:%d\n",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("%s\n",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] = '\0';
WSACleanup( );
}
3.服务器程序
(1)除accept函数处理部分tcp协议初始化步骤与客户端相同,这里不再赘述
(2)将accept函数放在一个for循环中接受多个客户端连接,NUM是一个宏定义的参数,可以设置最大用户接入数量
(3)设定用户名模块。使用字符串处理函数strcmp识别特殊字符,存储客户端用户名到一个指针数组,strcat可以将用户名拼接到用户列表中
(4)私聊模块。服务器通过识别特殊字符开启私聊模式,因为我设计程序时用户名指针数组跟socket的下标是对应的,利用这个特性可以用循环识别客户端需要私聊的用户,发送给对于socket[]数组中的一个就实现了私聊,输入退出私聊时服务器也可以识别,使用goto out:跳出这个模块。
(5)群聊模块。在群聊模式下,使用for循环将收到的客户端消息广播到所有客户端
文章来源:https://www.toymoban.com/news/detail-478757.html
(6)服务器端整体代码如下
#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:>%s\n",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(">>:%s\n",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("绑定错误:%d\n",GetLastError());
}
printf("绑定成功\n");
//5 监听
r = listen(serverSocket,10);
if(-1 == r) {
//8关闭socket
closesocket(serverSocket);
//9清理协议版本信息
WSACleanup();
printf("监听错误:%d\n",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("网络崩了%d\n",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] = '\0';
WSACleanup( );
}
看到这里的大佬们,代码我纯手打,可能存在bug,欢迎各位指正,也希望你们能为我点一个大大的赞!将这份知识传递下去! 文章来源地址https://www.toymoban.com/news/detail-478757.html
到了这里,关于基于TCP协议的聊天室详细教学(C++)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!