Win32下使用Socket:WinSock
来源:互联网 发布:2017淘宝生意好差 编辑:程序博客网 时间:2024/04/30 23:01
学习socket最好能有两台以上联网的电脑,以及能获得公网IP的网络接入方式。两年前,我主要使用的是一台win2k3和Debain Linux双系统的电脑,例外有台99年的老机器装着win98,而且没有装VC,测试相当的麻烦。现在买了笔记本,使用的是Vista的win32环境(32位),可以直接和老电脑的Linux联网进行测试。另外,网络环境也换成了电信的ADSL,贵了很多,为的就是能有一个公网IP。接下来的教程我会兼顾winsock的代码,这主要是因为winsock本身对socket几乎是兼容的。所以,这里有必要先说明在VC环境中使用socket的一些简单设置,以及与Linux环境下的细微差别。
我的VC环境依然是2008 Express,在写这篇教程的时候,微软已经发布了VC 2010,目前在微软的官方主页,提供了VC 2010的下载,同时保留着VC 2008的下载。
我们在VC中建立一个控制台的空项目:
我们着手构建自己的第一个winsock程序。
首先win32下与Linux下的socket API需要包含不同的头文件。
在Linux下是这些:
#include <sys/socket.h>
#include <arpa/inet.h>
这可能可以兼容非常古老的版本中的winsock,比如win98,而微软官方所推荐的是:
那么,什么是winsock的版本?这就涉及到winsock的初始化函数WSAStartup:
http://msdn.microsoft.com/en-us/library/ms742213(v=VS.85).aspx
上面是微软的官方说明,我这里构建一个简单的类,希望每次使用的时候引入一个类对象就可以了。
private:
WSADATA wsaData;
public:
WinsockAPI(int low_byte = 2, int high_byte = 2);
~WinsockAPI();
void showVersion() const;
};
WinsockAPI::WinsockAPI(int low_byte, int high_byte)
{
const WORD wVersionRequested = MAKEWORD(low_byte, high_byte);
int wsa_startup_err = WSAStartup(wVersionRequested, &wsaData);
if (wsa_startup_err != 0) {
std::cerr << "WSAStartup() failed." << std::endl;
throw wsa_startup_err;
}
}
WinsockAPI::~WinsockAPI()
{
WSACleanup();
}
void WinsockAPI::showVersion() const
{
std::cout << "The version of Winsock.dll is "
<< int(LOBYTE(wsaData.wVersion))
<< "."
<< int(HIBYTE(wsaData.wVersion))
<< "."
<< std::endl;
return;
}
首先,宏MAKEWORD()将两个int转换为winsock形式的版本号,我这里默认是是2.2,就只需要MAKEWORD(2, 2),如果是老版本的,最低应该是1.0。WSAStartup()将winsock的初始化信息写入一个WSADATA结构(我们这里的wsaData),如果成功返回0,失败将返回一个int的错误代码。这个错误代码直接表示了错误信息,微软官方建议不使用winsock的通用异常信息获取函数WSAGetLastError()获取WSAStartup()的错误信息,这可能是因为如果WSAStartup()失败,那么winsock的错误信息不一定能够正确的构建出来的缘故。
最后,winsock结束后用WSACleanup()清理。
因为socket本身的复杂性,异常信息提示非常重要。WSAGetLastError()的官方说明如下:
http://msdn.microsoft.com/en-us/library/ms741580(VS.85).aspx
错误代码所反馈的信息查询在这里:
http://msdn.microsoft.com/en-us/library/ms740668(v=VS.85).aspx
最后,需要注意的问题是,因为socket是构建在UNIX系统下的(BSD socket是当今所有socket的基础),所以socket很好的利用了UNIX体系“一切都是文件”的性质,每个socket本身也就是一个UNIX文件描述符,因此,Linux下的socket是用关闭文件的函数close()关闭的。但是win32下没这个性质,所以winsock是另外一种抽象,但是好在同样用int作为描述符,关闭需要专门为winsock定做的函数closesocket()。
下面重写了TCP Server的代码(类的抽象和构造也重新写了,将在下一章解释),作为winsock使用的演示。
#ifndef SOCK_CLASS_HPP
#define SOCK_CLASS_HPP
#include <iostream>
#include <winsock2.h>
namespace sockClass
{
void error_info(const char* s);
}
class WinsockAPI{
private:
WSADATA wsaData;
public:
WinsockAPI(int low_byte = 2, int high_byte = 2);
~WinsockAPI();
void showVersion() const;
};
class BaseSock{
protected:
int sockFD;
public:
BaseSock();
virtual ~BaseSock() = 0;
const int& showSockFD() const;
};
class TCPListenSock: public BaseSock{
private:
sockaddr_in listenSockAddr;
public:
TCPListenSock(unsigned short listen_port);
~TCPListenSock();
void TCPListen(
int max_connection_requests = 10) const;
};
class TCPServerSock: public BaseSock{
private:
sockaddr_in clientSockAddr;
protected:
char* preBuffer;
int preBufferSize;
mutable int preReceivedLength;
public:
TCPServerSock(
const TCPListenSock& listen_sock,
int pre_buffer_size = 32);
virtual ~TCPServerSock();
int TCPReceive() const;
int TCPSend(const char* send_data,
const int& data_length) const;
};
#endif //SockClass.hpp
#include "SockClass.hpp"
//sockClass
namespace sockClass
{
void error_info(const char* s)
{
std::cerr << s << std::endl;
throw WSAGetLastError();
}
}
//class WinsockAPI
WinsockAPI::WinsockAPI(int low_byte, int high_byte)
{
const WORD wVersionRequested = MAKEWORD(low_byte, high_byte);
int wsa_startup_err = WSAStartup(wVersionRequested, &wsaData);
if (wsa_startup_err != 0) {
std::cerr << "WSAStartup() failed." << std::endl;
throw wsa_startup_err;
}
}
WinsockAPI::~WinsockAPI()
{
WSACleanup();
}
void WinsockAPI::showVersion() const
{
std::cout << "The version of Winsock.dll is "
<< int(LOBYTE(wsaData.wVersion))
<< "."
<< int(HIBYTE(wsaData.wVersion))
<< "."
<< std::endl;
return;
}
//class BaseSock
BaseSock::BaseSock():
sockFD(-1)
{}
BaseSock::~BaseSock()
{}
const int& BaseSock::showSockFD() const
{
return sockFD;
}
//class TCPListenSock
TCPListenSock::TCPListenSock(unsigned short listen_port)
{
sockFD = socket(PF_INET,
SOCK_STREAM,
IPPROTO_TCP);
if (sockFD < 0) {
sockClass::error_info("socket() failed.");
}
memset(&listenSockAddr, 0, sizeof(listenSockAddr));
listenSockAddr.sin_family = AF_INET;
listenSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
listenSockAddr.sin_port = htons(listen_port);
if (bind( sockFD,
(sockaddr*)&listenSockAddr,
sizeof(listenSockAddr)) < 0) {
sockClass::error_info("bind() failed.");
}
}
TCPListenSock::~TCPListenSock()
{
closesocket(sockFD);
}
void TCPListenSock::TCPListen(
int max_connection_requests) const
{
if (listen( sockFD,
max_connection_requests) < 0) {
sockClass::error_info("listen() failed.");
}
}
//class TCPServerSock
TCPServerSock::TCPServerSock(
const TCPListenSock& listen_sock,
int pre_buffer_size):
preBufferSize(pre_buffer_size),
preReceivedLength(0)
{
preBuffer = new char[preBufferSize];
int clientSockAddrLen = sizeof(clientSockAddr);
sockFD = accept( listen_sock.showSockFD(),
(sockaddr*)&clientSockAddr,
&clientSockAddrLen);
if (sockFD < 0) {
sockClass::error_info("accept() failed.");
}
std::cout << "Client (IP: "
<< inet_ntoa(clientSockAddr.sin_addr)
<< ") conneted." << std::endl;
}
TCPServerSock::~TCPServerSock()
{
delete [] preBuffer;
closesocket(sockFD);
}
int TCPServerSock::TCPReceive() const
{
preReceivedLength = recv( sockFD,
preBuffer,
preBufferSize,
0);
if (preReceivedLength < 0) {
sockClass::error_info("recv() failed.");
} else if (preReceivedLength == 0) {
std::cout << "Client has been disconnected./n";
return 0;
}
return preReceivedLength;
}
int TCPServerSock::TCPSend(const char* send_data,
const int& data_length) const
{
if (data_length > preBufferSize) {
throw "Data is too large, resize preBufferSize.";
}
int sent_length = send( sockFD,
send_data,
data_length,
0);
if (sent_length < 0) {
sockClass::error_info("send() failed.");
} else if (sent_length != data_length) {
sockClass::error_info("sent unexpected number of bytes.");
}
return sent_length;
}
#ifndef APP_SOCK_HPP
#define APP_SOCK_HPP
#include "SockClass.hpp"
class TCPEchoServer: public TCPServerSock{
public:
TCPEchoServer(
const TCPListenSock& listen_sock,
int pre_buffer_size = 32);
~TCPEchoServer();
bool handEcho() const;
};
#endif //AppSock.hpp
#include <string>
#include "AppSock.hpp"
TCPEchoServer::TCPEchoServer(const TCPListenSock& listen_sock, int pre_buffer_size):
TCPServerSock(listen_sock, pre_buffer_size)
{}
TCPEchoServer::~TCPEchoServer()
{}
bool TCPEchoServer::handEcho() const
{
const std::string SHUTDOWN_CMD = "/shutdown";
while (TCPReceive() > 0) {
std::string cmd(preBuffer, SHUTDOWN_CMD.size());
if (cmd == SHUTDOWN_CMD && preReceivedLength == SHUTDOWN_CMD.size()) {
return false;
}
TCPSend(preBuffer, preReceivedLength);
}
return true;
}
#include "SockClass.hpp"
#include "AppSock.hpp"
int TCP_echo_server(int argc, char* argv[]);
int main(int argc, char* argv[])
{
int mainRtn = 0;
try {
mainRtn =TCP_echo_server(argc, argv);
}
catch (const char* s) {
perror(s);
return 1;
}
catch (const int& err) {
std::cerr << "Error: " << err << std::endl;
return 1;
}
return mainRtn;
}
int TCP_echo_server(int argc, char* argv[])
{
const unsigned short DEFAULT_PORT = 5000;
unsigned short listen_port = DEFAULT_PORT;
if (argc == 2 && atoi(argv[1]) > 0) {
listen_port = atoi(argv[1]);
}
WinsockAPI winsockInfo;
winsockInfo.showVersion();
TCPListenSock listen_sock(listen_port);
listen_sock.TCPListen();
bool go_on = true;
while (go_on){
TCPEchoServer echo_server(listen_sock);
go_on = echo_server.handEcho();
}
return 0;
}
- Win32下使用Socket:WinSock
- Socket通信——Windows下,使用Winsock,C++
- win32下Socket编程(1)
- win32下Socket编程(1)
- win32下C++ Socket编程
- Winsock使用之创建客户端Socket
- 基于Win32平台下Winsock API的网络编程
- [win32] Winsock 中应使用 DisconnectEx 关闭连接
- WINSOCK Win32 TCP通信
- win32下使用pthread
- win32 下使用数据库
- socket编程之select()-win32下
- 【转】Win32下的socket编程
- 如何使用WINSOCK Api hook拦截修改socket数据包
- 如何使用WINSOCK Api hook拦截修改socket数据包
- winsock.h(Socket编程)
- Winsock API:socket
- winsock使用
- OpenCV来控制鼠标移动
- 课表
- Java Exception处理之最佳实践
- VS2005下配置OpenCV2.1
- C如何调用C++的库
- Win32下使用Socket:WinSock
- 如何在VS2008中调用DLL/如何在VS2008中生成DLL
- Linux下Socket编程之TCP应用
- jquery $.ajax 全局事件引用方式以及各个事件(全局/局部)执行顺序
- Fedora 13下Apache,MySQL,PHP二进制编译安装
- CSerialPort类的源码(程序文件和头文件)
- Linux下Socket编程之UDP原理
- 第一次申请了我的Csdn论坛帐号
- Linux下Socket编程之UDP应用