服务器开发—Socket通信实例(一)

今天是我第一次开始写博,之前一直在看博,学到了很多东西,却懒得写博,会觉得有点费时间,做为程序员,尤其是像我这种屌丝,最近要严格要求自己经常写博,因为写博是一种奉献精神,同时也能提高自己的写作水平,遇到的错误也会得到广大博友的指正,总之好处多多,本人文笔较差,并有少许错字(少壮不努力),希望得到各位博者的指正。下面来介绍我最近自学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;

 

好了本文到此结了,之后文章还有客户的请求连接,发/接数据,以及解析/加密数据等实例,到每完成一个大功能模块后,会将整个项目打包给大家下载!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值