今天突然想写个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();
}
好啦,所有代码都上传了,大家不用再用分去下载啦,都是很简单的实现,主要是自己一直想试着写写,大神们不要笑话我了,不过希望有大神看了能推荐我看看哪些书,谢谢各位啦~
如果有哪里看不懂,或者哪里有问题,欢迎评论指正,我会虚心接受的~
谢谢各位~