今天是我第一次开始写博,之前一直在看博,学到了很多东西,却懒得写博,会觉得有点费时间,做为程序员,尤其是像我这种屌丝,最近要严格要求自己经常写博,因为写博是一种奉献精神,同时也能提高自己的写作水平,遇到的错误也会得到广大博友的指正,总之好处多多,本人文笔较差,并有少许错字(少壮不努力),希望得到各位博者的指正。下面来介绍我最近自学C++的相关实例拿出来跟大家分享,这是一个关于Socket通信这方面的实例,既然用到了C++就要用OO思想,好了,我废话少说了,先看下面:
Socket.h
/*
* Socket.h
*
* Created on: 2012-9-28
* Author: root
*/
#ifndef SOCKET_H_
#define SOCKET_H_
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
const int CONNECTION_MAX = 500;
const int RECEIVE_BUF_MAX = 4096;
namespace gateway{
class Socket {
public:
Socket();
virtual ~Socket();
// Server initialization
bool Create();// create a socket
bool Bind(const int port);
bool Listen() const;
bool Accept(Socket& clientSocket) const;
// Client initialization
bool Connet(const std::string& host,const int port);
// Data Transmissioin
static bool Send(Socket& socket,const std::string message) ;
static int Recevie(Socket& socket,std::string& message);
void SetNonBlocking(const bool flag);
bool IsValied() const;
int GetAddress() const;
int GetPost() const;
private :
// use m_sockfd to record the result of function socket
int m_sockfd;
struct sockaddr_in m_address;
};
}
#endif /* SOCKET_H_ */
这个头文件主要定义了一些基本接口,对新老鸟来说,这一看就懂,所以我就择重点来讲解,大家都知道,指针与引用都可以调用对象中的方法,那什么时候用哪个好呢?比如像:static bool Send(Socket& socket, const std::string message) 为什么用引用呢,好,我直言了,引用比指针速度快(指针还要查找地址),所以能优先用引用就先用引用,别外引用不能为空,指针可以,当调用者有可能传空,但你又不要他传空,这时你用引用做参数,这样也保证了接口的安全;指针比较适合经常对象删除增加的接口里,比较要调同类对象的某个方法,那有可能这个对象不存在这个容器中,这里可以返回NULL;
下面再来看看Socket.cpp的实现
Socket.cpp
/*
* Socket.cpp
*
* Created on: 2012-9-28
* Author: root
*/
#include "Socket.h"
#include <memory.h>
#include <iostream>
#include <fcntl.h>
namespace gateway{
Socket::Socket() :
m_sockfd(-1) {
}
Socket::~Socket() {
if (IsValied()) {
::close(m_sockfd);
}
}
/**
* server function
*/
bool Socket::Create() {
m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (!IsValied()) {
return false;
} else {
return true;
}
}
/**
* bind function
*/
bool Socket::Bind(const int port) {
m_address.sin_family = AF_INET;
m_address.sin_addr.s_addr = htonl(INADDR_ANY );
m_address.sin_port = htons(port);
int bindReturn = ::bind(m_sockfd, (struct sockaddr *) &m_address,
sizeof(m_address));
return bindReturn != -1;
}
/**
* listen function
*/
bool Socket::Listen() const {
if (!IsValied()) {
return false;
}
int listenReturn = ::listen(m_sockfd, CONNECTION_MAX);
return listenReturn != -1;
}
/**
* Accept function
*/
bool Socket::Accept(Socket& clientSocket) const {
int clientaddrLength = sizeof(clientSocket.m_address);
clientSocket.m_sockfd = ::accept(m_sockfd,
(struct sockaddr*) &clientSocket.m_address,
(socklen_t *) &clientaddrLength);
return clientSocket.m_sockfd != -1;
}
/**
* Connet function
*/
bool Socket::Connet(const std::string& host, int port) {
if (!IsValied()) {
return false;
}
m_address.sin_family = AF_INET;
m_address.sin_port = htons(port);
m_address.sin_addr.s_addr = inet_addr(host.c_str());
int connectReturn = ::connect(m_sockfd, (struct sockaddr *) &m_address,
sizeof(m_address));
return connectReturn != -1;
}
/**
* Send function
*/
bool Socket::Send(Socket& socket, const std::string message) {
int sendReturn = ::send(socket.m_sockfd, message.c_str(), message.length(),
MSG_NOSIGNAL);
return sendReturn != -1;
}
/**
* Recevie function
*/
int Socket::Recevie(Socket& socket, std::string& message) {
char buffer[RECEIVE_BUF_MAX + 1];
message.clear();
memset(buffer, 0, RECEIVE_BUF_MAX + 1);
int recvReturn = ::recv(socket.m_sockfd, buffer, RECEIVE_BUF_MAX, 0);
if (recvReturn == -1) {
std::cout << "error in Socket::Receive\n" << std::endl;
} else if (recvReturn > 0) {
message = buffer;
}else if(recvReturn==0){
std::cout << "shut down" << std::endl;
}
return 0;
}
/**
* Set NonBlocking function
*/
void Socket::SetNonBlocking(const bool flag) {
if (IsValied()) {
int opts = fcntl(m_sockfd, F_GETFL);
if (opts < 0) { // error
return;
}
if (flag) {
opts = (opts | O_NONBLOCK);
} else {
opts = (opts | ~O_NONBLOCK);
}
}
}
/**
* isvalid function
*/
bool Socket::IsValied() const {
return m_sockfd != -1;
}
int Socket::GetAddress() const {
return m_address.sin_addr.s_addr;
}
int Socket::GetPost() const {
return m_address.sin_port;
}
}
这个实现类也比较简单了,所以简述下:Socket服务端通信过程如下: 创建Socket(套接字)->绑定端口->监听端口->等待客户端连接->接收/发送数据->断开连接
在这里我想对Socket服务不了解的菜鸟解析下,创建、绑定,相信是能理解的,我说下等待连接,即运行到这行代码时,这时服务器是一直在等待的,要等到有客户端连接才往下走,这时服务端已经是拿到客户端的地址(Socket),可以对它发送可接收数据,发送很简单,不说了,接收也是一直在等待,直到有客户端发送数据过来才往下运行,如此重复就可以对这个客户端进行交流了;菜鸟可能有疑问,那是不是只能有一个客户的,不是的,我们可以第连接到一个客户就分配一个线程给它,这里就可以多用户连接进来了,当然分配进程也可以。(这里介绍的是非NIO模式)
下面来看一下服务器的ServerSocket类
ServerSocket.h
/*
* ServerSocket.h
*
* Created on: 2012-9-28
* Author: root
*/
#ifndef SERVERSOCKET_H_
#define SERVERSOCKET_H_
#include "Socket.h"
#include <list>
#include <semaphore.h>
#include "ThreadReadWriteLock.h"
const int RECV_WAIT_TIME = 100;
const int CONNECT_DIFFER_TIME = 100;
using std::list;
namespace gateway{
class ServerSocket : public Socket{
public:
ServerSocket(const int port);
virtual ~ServerSocket();
bool Accept();
// run server to connect multi-clients
void Run();
void Accept(Socket& socket);
void AddClient(Socket* clientSocket);
static void* ProcessMessage(void* arg);
private :
// accetp multi-clients
static void DeleteClient(Socket* clientSocket);
// static void* ProcessMessage(void* arg);
static void SendMsgToAllUsers(const std::string& message);
static list<Socket*> clientSockets;
static bool serviceFlag;
// use thread-read-write-lock to synchronize threads
static ThreadReadWriteLock readWriteLock;
};
}
#endif /* SERVERSOCKET_H_ */
这个头文件也比较简单,注意下static 变量,还有void AddClient(Socket* clientSocket)这里用的是批量参数,知道为什么用指引参数吗?呵呵,上面已经有介绍,因为指针可以为空,删除,增加,调用对象方法,是为空也不怕,如果是引用为空那就不好搞了
ServerSocket.cpp
/*
* ServerSocket.cpp
*
* Created on: 2012-9-28
* Author: root
*/
#include "ServerSocket.h"
#include "SocketException.h"
#include <pthread.h>
#include <iostream>
#include "Encoder.h"
#include "ServerHandler.h"
namespace gateway{
using namespace std;
/**
* static vars must init at here
*/
list<Socket*> ServerSocket::clientSockets;
ThreadReadWriteLock ServerSocket::readWriteLock;
bool ServerSocket::serviceFlag = true;
ServerSocket::ServerSocket(const int port) {
if (!Socket::Create()) {
throw SocketException("Could not create server socket.");
}
if (!Socket::Bind(port)) {
throw SocketException("Could not bind to port.");
}
if (!Socket::Listen()) {
throw SocketException("Could not Listen to socket.");
}
}
void ServerSocket::Accept(Socket& socket) {
if (!Socket::Accept(socket)) {
throw SocketException("Could not accpet socket.");
}
}
bool ServerSocket::Accept() {
Socket* clientSocket = new Socket;
/**
* why has a '*' on the clientSocket before
* because add a '*' is the pointer has a value that &
*/
Accept(*clientSocket);
AddClient(clientSocket);
// create a new thread for a new client
pthread_t newThread;
int result=pthread_create(&newThread, NULL, ProcessMessage,static_cast<void*>(clientSocket));
return result == 0;
}
void* ServerSocket::ProcessMessage(void* arg) {
std::cout << "thread..." << std::endl;
std::string message;
Socket* clientSocket = static_cast<Socket*>(arg);
while (serviceFlag) {
std::cout << "thread2..." << std::endl;
// sleep(2);
// Socket::Send(*clientSocket, "Welcome!");
Socket::Recevie(*clientSocket, message);
std::cout << "Recv from Client["<< clientSocket->GetAddress() << ":" << clientSocket->GetPost()<<"]:" << message << std::endl;
ServerHandler::doAction(message);
// Socket::Send(*clientSocket,Encoder::encoder("BBBBBBBBB"));
message.clear();
// usleep(RECV_WAIT_TIME);
}
pthread_exit(NULL);
return NULL;
}
void ServerSocket::AddClient(Socket* clientSocket) {
if (readWriteLock.SetReadLock()) {
clientSockets.push_back(clientSocket);
std::cout << "How " << clientSockets.size() << " users" << std::endl;
std::cout << "New User " << std::endl;
readWriteLock.UnLock();
} else {
serviceFlag = false;
}
}
void ServerSocket::Run() {
while (serviceFlag) {
if (clientSockets.size() >= static_cast<unsigned int>(CONNECTION_MAX)) {
serviceFlag = false;
} else {
serviceFlag = Accept();
}
usleep(CONNECT_DIFFER_TIME);
}
}
void ServerSocket::DeleteClient(Socket* socketClient) {
if (readWriteLock.SetWriteLock()) {
list<Socket*>::iterator iter;
for (iter = clientSockets.begin(); iter != clientSockets.end();
iter++) {
if ((*iter)->GetAddress() == socketClient->GetAddress()
&& (*iter)->GetPost() == socketClient->GetPost()) {
// delete this client socket
delete (*iter);
clientSockets.erase(iter);
std::cout << "Now " << clientSockets.size() << " users;\n" << std::endl;
break;
}
}
}else{
serviceFlag=false;
}
}
ServerSocket::~ServerSocket() {
// TODO Auto-generated destructor stub
}
}
这个文件对于菜鸟来说有点难度了,首先,要建立OO思想,比如将创建,绑定和鉴定封装到一个方法里,如果出错错误时,直接将错误信息抛出来,本人特别喜欢抛错这种机制,因为它可以层层往上抛,一般软件的错误报错都是采用这种模式,有错就停止了,这里还要注意的是头文件定义的静态常量,它在实现类中必须要初始化的,或者编译是通不过的:list<Socket*> ServerSocket::clientSockets;
ThreadReadWriteLock ServerSocket::readWriteLock;
bool ServerSocket::serviceFlag = true;
好了本文到此结了,之后文章还有客户的请求连接,发/接数据,以及解析/加密数据等实例,到每完成一个大功能模块后,会将整个项目打包给大家下载!