C++网络编程简单示例 基于socket通信的控制台聊天软件

今天突然想写个c++的通过socket来连接的 服务器端 和 客户端代码,刚写完一部分 先分享上来吧!

中途一直遇到accept没有阻塞的问题,后来发现了是要使用WSAStartup函数来指明使用的版本,要不然就使用不了socket函数!唉,研究了半个多小时才发现。。。坑啊!

1、WSAStartup函数
                int WSAStartup
                       (
                          WORD wVersionRequested,
                          LPWSADATA lpWSAData
                         );
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
eg:假如一个程序要使用2.1版本的Socket,那么程序代码如下

wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData ); 

2、WSACleanup函数
int WSACleanup (void);
应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。


3、通信用的数据结构

class MessageClass
{
public:
	MessageClass(char* in){
		strcpy(data,in);
		GetLocalTime(&sysTime);   
	}
	MessageClass(){}
	SYSTEMTIME sysTime;  //发送时间
	char data[100];    //数据
	char toWho[16];   //发给谁
	bool toOne;     //是否私聊
protected:
private:
};




这两天抽时间终于把它完成了,首先写了一个类来完成账号、密码登陆以及检查,现在账号密码是用文件的格式存的,

比较初级,如果大家有高级点的方式,可以告诉我,我去学习学习,先谢谢大家啦~

首先这个类的代码贴出来:

这里使用了单例模式,单例模式的好处大家都懂的就不再叙述了,要是有朋友不了解可以进我的另一篇文章,设计模式之单例模式,了解下~

其他的代码的注释也解释了,应该没什么疑问~

/* 
 *@author: LiuCimin   
 *web:     http://blog.csdn.net/liucimin/
 *mail:    lcmjk@foxmail.com 
 *2014-9-03
 */  

#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
class LoadUsers
{
public:
	static LoadUsers* getUsers()
	{
		if (loadusers==NULL)
		{
			return new LoadUsers();
		}
		else
			return loadusers;
	}

	///从文件中读取数据,加载已经注册的用户名和密码;
	void getUsers(string filename)
	{
		try
		{
			//ifstream indatas(address);
			//string tempdata;
			ifstream file(filename);
			string userLine;
			//file.open(filename,ios::out);
			if(!file)
			{
				throw "用户数据文件不存在,请检查是否存在";

			}

			while (getline(file,userLine))
			{
				string i1=userLine.substr(0,userLine.find_first_of(' '));
				string i2=userLine.substr(userLine.find_first_of(' ')+1,userLine.size());
				m.insert(make_pair<string,string>(i1,i2));

			}
		}
		catch(char* &err)
		{
			cout<<err<<endl;
		}

	}

	///登录时检查用户名和增加已经登录名单;
	bool checkAndadd(string username,string password)
	{
		if (findUser(username))
		{
			return false;    //已经登录
		}
		else
		{
			if( (m.find(username)!=m.end()))
			{
				//string in=m.find(username)->second;
			if ((m.find(username)->second)==password)
			{
				v.push_back(username);
				return true;
			}
			else
			{
				return false;   //用户名不存在 或密码不对
			}
			}
			else
			{
				return false;   //用户名不存在 或密码不对
			}
		}
	}



	///从已经登录的用户名和密码查找是否已经登录;
	bool findUser(string username)
	{
		vector<string>::iterator it = find(v.begin(), v.end(), username);
		if (it != v.end()) // 找到了
		{
			return true;
		}
		else // 没找到
		{
			return false;
		}
	}

	///从已经登录的用户名和密码删除断线用户;
	bool deleteUser(char* in)
	{
		for (vector<string>::iterator i=v.begin();i!=v.end();i++)
		{
			if (strcmp(in,(*i).c_str())==0)
			{
				v.erase(i);
				return true;
				break;
			}
		}
		
	}
private:
	LoadUsers(){};  //单例模式
	static LoadUsers * loadusers;
	static map<string,string> m;
	static vector<string> v;
	
};
LoadUsers * LoadUsers::loadusers=NULL;
map<string,string> LoadUsers::m;
vector<string> LoadUsers::v;


其次就是服务器端的代码:

暂时没有优化,大家凑合看吧~嘿嘿~

/* 
 *@author: LiuCimin   
 *web:     http://blog.csdn.net/liucimin/
 *mail:    lcmjk@foxmail.com 
 *2014-9-03
 */  
#include <iostream>
#include <fstream>
#include <string>
#include"LoadUsers.h"
#include <map>
#include <Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;


LoadUsers* loadusers;
//用来存储返回的新的用于通信的套接字 
multimap<SOCKET, char*> socketMap;

///一个用来发送接收的数据结构
///内有发送时间、数据、
class MessageClass
{
public:
	MessageClass(char* in){
		strcpy(data,in);
		GetLocalTime(&sysTime);   
	}
	MessageClass& operator=(char* in){
		strcpy(data,in);
		GetLocalTime(&sysTime);   
		return *this;
	}
	MessageClass(){}

	SYSTEMTIME sysTime;
	char data[100];
	char toWho[16];
	bool toOne;
protected:
private:
};
//等待线程的函数
DWORD WINAPI RecData(LPVOID lparam)
{ 

	SOCKET client = (SOCKET)lparam; 
	MessageClass messageData;

	while (1)
	{
		memset((char*)&messageData, 0, sizeof(messageData));  
		if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) != SOCKET_ERROR) 
		{
			cout<<messageData.sysTime.wHour<<':'<<messageData.sysTime.wMinute<<':'<<messageData.sysTime.wSecond<<endl;  
			if(messageData.toOne==false)    //私聊
			{
				if (socketMap.size()!=0)
				{
					char dataTemp[130];
					memset(dataTemp,0,130);
					strcat(dataTemp,messageData.data);
					for (multimap<SOCKET, char*>::iterator i=socketMap.begin();i!=socketMap.end();i++)
					{
					
						if (i->first!=client)
						{
							char otherClientAddr[130];
							memset(otherClientAddr,0,130);
							strcpy(otherClientAddr,socketMap.find(client)->second);
							strcat(otherClientAddr,":");
							strcat(otherClientAddr,dataTemp);
							strcpy(messageData.data,otherClientAddr);
							send(i->first,(char*)&messageData, sizeof(messageData)+1,0);
						}
					}
				}
			}
			else                              //公聊
			{
				for (multimap<SOCKET, char*>::iterator i=socketMap.begin();i!=socketMap.end();i++)
				{
					if (strcmp(i->second,messageData.toWho)==0)
					{
						char otherClientAddr[130];
						strcpy(otherClientAddr,socketMap.find(client)->second);
						strcat(otherClientAddr,"(私)");
						strcat(otherClientAddr,":");
						strcat(otherClientAddr,messageData.data);
						strcpy(messageData.data,otherClientAddr);
						send(i->first,(char*)&messageData, sizeof(messageData)+1,0);

					}
				}
			}
			

		}
		else
		{
			cout<<"connect error";
			LoadUsers::getUsers()->deleteUser(socketMap.find(client)->second);              //连接中断,已登录账户数据清除
			socketMap.erase(socketMap.find(client));
			break;
		}
	}
	closesocket(client);  
	return 0;  

}


//专门用来接收连接账号密码的线程函数
DWORD WINAPI ConnetctCheck(LPVOID lparam)
{
	SOCKET client=(SOCKET)lparam;
	char password[16]={0};
	char username[16]={0};
	MessageClass messageData;
	messageData="请输入用户名";
	if(send(client,(char*)&messageData, sizeof(messageData)+1,0) != SOCKET_ERROR)  
	{  
		cout<<"有客户请求连接,正在等待输入用户名!"<<client<<endl;  
		//连接中断重新accept
		if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) == SOCKET_ERROR) 
			return 0;
		strcpy(username,messageData.data);
	} 
	messageData="请输入密码";
	if(send(client,(char*)&messageData, sizeof(messageData)+1,0) != SOCKET_ERROR)  
	{  
		cout<<"有客户请求连接,正在等待输入密码!"<<client<<endl;  
		//连接中断重新accept
		if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) == SOCKET_ERROR) 
			return 0;
		strcpy(password,messageData.data);
	} 


	while(!loadusers->checkAndadd(username,password))    //当登录不成功时重新接收
	{
		messageData="用户名或密码错误或者已经登录此账号,请重新输入";
		send(client,(char*)&messageData, sizeof(messageData)+1,0);
		memset(password,0,16);
		memset(username,0,16);
		messageData="请输入用户名";
		if(send(client,(char*)&messageData, sizeof(messageData)+1,0) != SOCKET_ERROR)  
		{  
			cout<<"有客户请求连接,正在等待输入用户名!"<<client<<endl;  
			//连接中断重新accept
			if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) == SOCKET_ERROR) 
				return 0;

			strcpy(username,messageData.data);
		} 
		messageData="请输入密码";
		if(send(client,(char*)&messageData, sizeof(messageData)+1,0) != SOCKET_ERROR)  
		{  
			cout<<"有客户请求连接,正在等待输入密码!"<<client<<endl;  
			//连接中断重新accept
			if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) == SOCKET_ERROR) 
				return 0;
			strcpy(password,messageData.data);
		} 
	}
	messageData="口令正确,连接成功";
	send(client,(char*)&messageData, sizeof(messageData)+1,0);
	char* usernameTemp=new char[16];
	strcpy(usernameTemp,username);
	socketMap.insert(make_pair(client,usernameTemp));
	HANDLE thread;
	DWORD  ThreadID;
	thread=CreateThread(0,0,RecData, (LPVOID)client,0,&ThreadID);
	return 0;  
}

void main(int argc, char argv[])
{
	//加载服务器中存的用户们
	loadusers=LoadUsers::getUsers();
	loadusers->getUsers("users.txt");



	//在使用socket之前引用版本为2.2的dll
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 2, 2 );
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 2 ||
		HIBYTE( wsaData.wVersion ) != 2 ) {
			WSACleanup( );
			return;
	}


	//设置服务器端的ip和port,bind好后,listen client端
	SOCKET s;
	SOCKADDR_IN serverIP;
	s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	//ZeroMemory((char *)&serverIP,sizeof(serverIP));
	serverIP.sin_family = AF_INET;  
	serverIP.sin_port = htons(6000);  
	serverIP.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
	bind(s,(SOCKADDR*)&serverIP, sizeof(SOCKADDR));
	listen(s,5);

	SOCKET client;
	SOCKADDR_IN clientAddr;  
	int length = sizeof(SOCKADDR);  
	while (true)
	{

		if(client=accept(s,(SOCKADDR*)&clientAddr,&length))
		{
			HANDLE thread;
			thread=CreateThread(0,0,ConnetctCheck, (LPVOID)client,0,NULL);
		//	TerminateThread(thread,NULL);
		}
	}
}


之后这部分是 客户端的代码

/* 
 *@author: LiuCimin   
 *web:     http://blog.csdn.net/liucimin/
 *mail:    lcmjk@foxmail.com 
 *2014-9-03
 */ 
#include <iostream>
#include <string>
#include <map>
#include <regex> 
#include <Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
///一个用来发送接收的数据结构
///内有发送时间、数据、
class MessageClass
{
public:
	MessageClass(char* in){
		strcpy(data,in);
		GetLocalTime(&sysTime);   
	}
	MessageClass(){}
	SYSTEMTIME sysTime;
	char data[100];
	char toWho[16];
	bool toOne;
protected:
private:
};

//接收到消息后显示出来线程
DWORD WINAPI RecData(LPVOID lparam)
{ 

	SOCKET client = (SOCKET)lparam;  
	MessageClass messageData;

	while (1)
	{
		memset((char*)&messageData, 0, sizeof(messageData));  
		if(recv(client, (char*)&messageData, sizeof(messageData)+1, 0) != SOCKET_ERROR) 
		{
			cout<<messageData.sysTime.wHour<<':'<<messageData.sysTime.wMinute<<':'<<messageData.sysTime.wSecond<<"  "<<messageData.data<<endl;  
		}
		else
		{
			cout<<"connect error";
			break;
		}
	}
	closesocket(client);  
	return 0;  

}

//连接服务器与发送消息模块
void connectServer()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 2, 2 );
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}
	if ( LOBYTE( wsaData.wVersion ) != 2 ||
		HIBYTE( wsaData.wVersion ) != 2 ) {
			WSACleanup( );
			return;
	}

	//设置服务器端的ip和port,bind好后,listen client端
	SOCKET s;
	SOCKADDR_IN serverIP;
	s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	//ZeroMemory((char *)&serverIP,sizeof(serverIP));
	serverIP.sin_family = AF_INET;  
	serverIP.sin_port = htons(6000);  
	serverIP.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");  
	connect(s,(SOCKADDR*)&serverIP,sizeof(serverIP));

	//启动线程来接收
	HANDLE thread;
	DWORD  ThreadID;
	thread=CreateThread(0,0,RecData, (LPVOID)s,0,&ThreadID);
	while (1)
	{
		MessageClass messageData;
		string in;
		getline(cin,in);
		memset((char*)&messageData, 0, sizeof(messageData));  
		//如果以/开头,则判断为私聊
		//再运用正则表达式判断是否给谁
		if (in[0]=='/')
		{
			tr1::regex pattern("(/\\w+ )");
			smatch result;
			bool match = regex_search(in, result, pattern);

			if(match)
			{
				string msg(result[0].first, result[0].second);
				strcpy(messageData.toWho,msg.substr(1,msg.size()-2).c_str());
				strcpy(messageData.data,in.substr(msg.size(),in.size()).c_str());
				messageData.toOne=true;
			}
		}
		else
		{
			strcpy(messageData.data,in.c_str());
			messageData.toOne=false;
		}
		GetLocalTime(&messageData.sysTime);
		send(s,(char*)&messageData, sizeof(messageData)+1,0);
		Sleep(100);
	}
}

void main(int argc, char argv[])
{
	connectServer();
}

好啦,所有代码都上传了,大家不用再用分去下载啦,都是很简单的实现,主要是自己一直想试着写写,大神们不要笑话我了,不过希望有大神看了能推荐我看看哪些书,谢谢各位啦~

如果有哪里看不懂,或者哪里有问题,欢迎评论指正,我会虚心接受的~

谢谢各位~


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值