#ifndef BASIC_MYLOCK_H
#define BASIC_MYLOCK_H
#ifdef _MSC_VER
#include <WinSock2.h> // 在每个windows.h 的include 前加 防止AF_IPX 重定义
#include "Windows.h"
//系统用的线程互斥锁。
namespace ITapManager
{
//线程同步类。是对Win32临界区对象的封装。
class CMyCriticalSection
{
public:
//CCriticalSection类的构造函数,初始化临界区,析构函数删除临界区。
CMyCriticalSection() { ::InitializeCriticalSection(&m_crit); }
~CMyCriticalSection() { ::DeleteCriticalSection(&m_crit); }
//进人临界区
void Enter() { ::EnterCriticalSection(&m_crit); }
//离开临界区
void Leave() { ::LeaveCriticalSection(&m_crit); }
private:
//内部线程互斥对象
CRITICAL_SECTION m_crit;
};
typedef CMyCriticalSection CMylock;
//临界区锁类。构造函数加锁,析构函数解锁。
class CMyGuard
{
public:
CMyGuard(CMylock &crit) : m_crit(crit) { m_crit.Enter(); }
~CMyGuard() { m_crit.Leave(); }
private:
//内部正真的线程互斥对象的引用。
CMylock & m_crit;
};
}
#endif // _MSC_VER
#ifdef __GNUC__
#include <pthread.h>
namespace ITapManager
{
class CMyCriticalSection
{
public:
//CCriticalSection类的构造函数,初始化临界区,析构函数删除临界区。
CMyCriticalSection()
{
::pthread_mutex_init( &m_mutex, NULL); //普通锁
}
~CMyCriticalSection()
{
::pthread_mutex_destroy( &m_mutex);
}
//进人临界区
void Enter() const
{
::pthread_mutex_lock( &m_mutex);
}
//离开临界区
void Leave()
{
::pthread_mutex_unlock( &m_mutex);
}
private:
//内部线程互斥对象
mutable pthread_mutex_t m_mutex;
};
typedef CMyCriticalSection CMylock;
//临界区锁类。构造函数加锁,析构函数解锁。
class CMyGuard
{
public:
CMyGuard(CMylock &crit) : m_crit(crit) { m_crit.Enter(); }
~CMyGuard() { m_crit.Leave(); }
private:
//内部正真的线程互斥对象的引用。
CMylock & m_crit;
};
}
#endif // __GNUC__
#endif // BASIC_MYLOCK_H
//=====================MyCriticalSection.h===========================
#ifndef _My_CRITICAL_SECTION_H
#define _My_CRITICAL_SECTION_H
#include <windows.h>
//对临界区同样进行封装
class CMyCriticalSection
{
public:
CMyCriticalSection()
{
InitializeCriticalSection(&m_cSection);
}
void Lock()
{
EnterCriticalSection(&m_cSection);
}
void UnLock()
{
LeaveCriticalSection(&m_cSection);
}
//利用析构函数删除临界区对象
virtual ~CMyCriticalSection()
{
DeleteCriticalSection(&m_cSection);
}
private:
CRITICAL_SECTION m_cSection;
};
class CCriticalSectionAutoLock
{
public:
//利用构造函数上锁,即进去临界区
CCriticalSectionAutoLock(CMyCriticalSection *mySection)
:pCMySection(mySection)
{
pCMySection->Lock();
}
//利用析构函数解锁,即离开临界区
virtual ~CCriticalSectionAutoLock()
{
pCMySection->UnLock();
}
private:
CMyCriticalSection *pCMySection;
};
#endif
---------------------
作者:蓝色枫叶
来源:CSDN
原文:https://blog.csdn.net/olansefengye1/article/details/53262917
版权声明:本文为博主原创文章,转载请附上博文链接!
https://blog.csdn.net/YF_Li123/article/details/72863927
服务器端代码 server.cpp:
[cpp]
客户端代码 client.cpp:
[cpp]
将 server.cpp 和 client.cpp 分别编译为 server.exe 和 client.exe,先运行 server.exe,再运行 client.exe,输出结果为:
Message form server: Hello World!
Windows 下的 socket 程序和 Linux 思路相同,但细节有所差别:
1) Windows 下的 socket 程序依赖 Winsock.dll 或 ws2_32.dll,必须提前加载。DLL 有两种加载方式,请查看:动态链接库DLL的加载
2) Linux 使用“文件描述符”的概念,而 Windows 使用“文件句柄”的概念;Linux 不区分 socket 文件和普通文件,而 Windows 区分;Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。
3) Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。
4) 关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数。
WinSock(Windows Socket)编程依赖于系统提供的动态链接库(DLL),有两个版本:
几乎所有的 Windows 操作系统都已经支持 ws2_32.dll,包括个人操作系统 Windows 95 OSR2、Windows 98、Windows Me、Windows 2000、XP、Vista、Win7、Win8、Win10 以及服务器操作系统 Windows NT 4.0 SP4、Windows Server 2003、Windows Server 2008 等,所以你可以毫不犹豫地使用最新的 ws2_32.dll。
使用DLL之前必须把DLL加载到当前程序,你可以在编译时加载,也可以在程序运行时加载,《C语言高级教程》中讲到了这两种加载方式,请猛击:动态链接库DLL的加载:隐式加载(载入时加载)和显式加载(运行时加载)。
这里使用#pragma命令,在编译时加载:
#pragma comment (lib, "ws2_32.lib")
WSAStartup() 函数
使用DLL之前,还需要调用 WSAStartup() 函数进行初始化,以指明 WinSock 规范的版本,它的原型为:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
wVersionRequested 为 WinSock 规范的版本号,低字节为主版本号,高字节为副版本号(修正版本号);lpWSAData 为指向 WSAData 结构体的指针。
关于 WinSock 规范
WinSock 规范的最新版本号为 2.2,较早的有 2.1、2.0、1.1、1.0,ws2_32.dll 支持所有的规范,而 wsock32.dll 仅支持 1.0 和 1.1。
wsock32.dll 已经能够很好的支持 TCP/IP 通信程序的开发,ws2_32.dll 主要增加了对其他协议的支持,不过建议使用最新的 2.2 版本。
wVersionRequested 参数用来指明我们希望使用的版本号,它的类型为 WORD,等价于 unsigned short,是一个整数,所以需要用 MAKEWORD() 宏函数对版本号进行转换。例如:
MAKEWORD(1, 2); //主版本号为1,副版本号为2,返回 0x0201
MAKEWORD(2, 2); //主版本号为2,副版本号为2,返回 0x0202
关于 WSAData 结构体
WSAStartup() 函数执行成功后,会将与 ws2_32.dll 有关的信息写入 WSAData 结构体变量。WSAData 的定义如下:
最后3个成员已弃之不用,szDescription 和 szSystemStatus 包含的信息基本没有实用价值,读者只需关注前两个成员即可。请看下面的代码:
运行结果:
wVersion: 2.2
wHighVersion: 2.2
szDescription: WinSock 2.0
szSystemStatus: Running
ws2_32.dll 支持的最高版本为 2.2,建议使用的版本也是 2.2。
综上所述:WinSock 编程的第一步就是加载 ws2_32.dll,然后调用 WSAStartup() 函数进行初始化,并指明要使用的版本号。
转载自:http://blog.csdn.net/ctrl_qun/article/details/52454232
来自 <https://blog.csdn.net/YF_Li123/article/details/72863927>
- }
- return 0;
- WSAStartup( MAKEWORD(2, 2), &wsaData);
- printf("wVersion: %d.%d\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
- printf("wHighVersion: %d.%d\n", LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));
- printf("szDescription: %s\n", wsaData.szDescription);
- printf("szSystemStatus: %s\n", wsaData.szSystemStatus);
- WSADATA wsaData;
- int main(){
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment (lib, "ws2_32.lib")
- } WSADATA, *LPWSADATA;
- char szSystemStatus[WSASYS_STATUS_LEN+1];
- unsigned short iMaxSockets; //2.0以后不再使用
- unsigned short iMaxUdpDg; //2.0以后不再使用
- char FAR *lpVendorInfo; //2.0以后不再使用
- //一个以 null 结尾的字符串,用来说明 ws2_32.dll 的状态以及配置信息
- char szDescription[WSADESCRIPTION_LEN+1];
- //一个以 null 结尾的字符串,用来说明 ws2_32.dll 的实现以及厂商信息
- WORD wVersion; //ws2_32.dll 建议我们使用的版本号
- WORD wHighVersion; //ws2_32.dll 支持的最高版本号
- typedef struct WSAData {
- 较早的DLL是 wsock32.dll,大小为 28KB,对应的头文件为 winsock1.h;
- 最新的DLL是 ws2_32.dll,大小为 69KB,对应的头文件为 winsock2.h。
- //初始化DLL
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2, 2), &wsaData);
- //创建套接字
- SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- //向服务器发起请求
- sockaddr_in sockAddr;
- memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
- sockAddr.sin_family = PF_INET;
- sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- sockAddr.sin_port = htons(1234);
- connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
- //接收服务器传回的数据
- char szBuffer[MAXBYTE] = {0};
- recv(sock, szBuffer, MAXBYTE, NULL);
- //输出接收到的数据
- printf("Message form server: %s\n", szBuffer);
- //关闭套接字
- closesocket(sock);
- //终止使用 DLL
- WSACleanup();
- system("pause");
- return 0;
- }
- int main(){
- #include <stdio.h>
- #include <stdlib.h>
- #include <WinSock2.h>
- #pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
- //初始化 DLL
- WSADATA wsaData;
- WSAStartup( MAKEWORD(2, 2), &wsaData);
- //创建套接字
- SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- //绑定套接字
- sockaddr_in sockAddr;
- memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
- sockAddr.sin_family = PF_INET; //使用IPv4地址
- sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
- sockAddr.sin_port = htons(1234); //端口
- bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
- //进入监听状态
- listen(servSock, 20);
- //接收客户端请求
- SOCKADDR clntAddr;
- int nSize = sizeof(SOCKADDR);
- SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
- //向客户端发送数据
- char *str = "Hello World!";
- send(clntSock, str, strlen(str)+sizeof(char), NULL);
- //关闭套接字
- closesocket(clntSock);
- closesocket(servSock);
- //终止 DLL 的使用
- WSACleanup();
- return 0;
- }
- int main(){
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)。
例如:
volatile int i=10;
int j = i;
...
int k = i;
volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。
/**********************
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
来自 <https://blog.csdn.net/Yqq__00/article/details/25142093>
#pragma comment
来自 <http://c.biancheng.net/cpp/html/2754.html>
也可以在编译时候再调试选项中加入库
WSASocket()
简述:创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组。
#include <winsock2.h>
SOCKET WSAAPI WSASocket ( int af, int type, int
protocol, LPPROTOCOL_INFO lpProtocolInfo, Group g,
int iFlags);
af:地址族描述。目前仅支持PF_INET格式,亦即ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。
g:套接口组的描述字。
iFlags:套接口属性描述。
返回值:
若无错误发生,WSASocket()返回新套接口的描述字。否则的话,返回 INVALID_SOCKET,应用程序可定调用WSAGetLastError()来获取相应的错误代码。
错误代码:
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSAEAFNOSUPPORT 不支持指定的地址族。
WSAEINPROGRESS 一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数
WSAEMFILE 无可用的套接口描述字。
WSAENOBUFS 无可用的缓冲区空间。套接口无法创建。
WSAEPROTONOSUPPORT 不支持指定的协议。
WSAEPROTOTYPE 指定的协议对于本套接口类型错误。
WSAESOCKTNOSUPPORT 本地址族不支持指定的套接口类型。
WSAEINVAL g参数非法。
---------------------
作者:wiSCADA
来源:CSDN
原文:https://blog.csdn.net/wiscada/article/details/3051722
版权声明:本文为博主原创文章,转载请附上博文链接!
bool tcpsocket()
{
#if defined WIN32 || defined WIN64
return socket(SOCK_STREAM, IPPROTO_TCP, WSA_FLAG_OVERLAPPED);
#endif
#ifdef linux
return socket(SOCK_STREAM, IPPROTO_TCP, 0);
#endif
}
bool socket(int type, int protocol, DWORD dwFlags)
{
#if defined WIN32 || defined WIN64
// 重叠 I/O
m_Sock = ::WSASocket(AF_INET, type, protocol, 0, 0, dwFlags);
//m_Sock = ::WSASocket(AF_INET, 1, IPPROTO_TCP, 0, 0, 0);
#endif
#ifdef linux
// I/O 复用
m_Sock = ::socket(AF_INET, type, protocol);
#endif
return (INVALID_SOCKET != m_Sock);
}
ioctlsocket()
简述:
控制套接口的模式。
#include <winsock.h>
int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp);
s:一个标识套接口的描述字。
cmd:对套接口s的操作命令。
argp:指向cmd命令所带参数的指针。
注释:
本函数可用于任一状态的任一套接口。它用于获取与套接口相关的操作参数,而与具体协议或通讯子系统无关。支持下列命令:
FIONBIO:允许或禁止套接口s的非阻塞模式。argp指向一个无符号长整型。如允许非阻塞模式则非零,如禁止非阻塞模式则为零。当创建一个套接口时,它就处于阻塞模式(也就是说非阻塞模式被禁止)。这与BSD套接口是一致的。WSAAsynSelect()函数将套接口自动设置为非阻塞模式。如果已对一个套接口进行了WSAAsynSelect() 操作,则任何用ioctlsocket()来把套接口重新设置成阻塞模式的试图将以WSAEINVAL失败。为了把套接口重新设置成阻塞模式,应用程序必须首先用WSAAsynSelect()调用(IEvent参数置为0)来禁至WSAAsynSelect()。
FIONREAD:确定套接口s自动读入的数据量。argp指向一个无符号长整型,其中存有ioctlsocket()的返回值。如果s是SOCKET_STREAM类型,则FIONREAD返回在一次recv()中所接收的所有数据量。这通常与套接口中排队的数据总量相同。如果S是SOCK_DGRAM 型,则FIONREAD返回套接口上排队的第一个数据报大小。
SIOCATMARK:确实是否所有的带外数据都已被读入。这个命令仅适用于SOCK_STREAM类型的套接口,且该套接口已被设置为可以在线接收带外数据(SO_OOBINLINE)。如无带外数据等待读入,则该操作返回TRUE真。否则的话返回FALSE假,下一个recv()或recvfrom()操作将检索“标记”前一些或所有数据。应用程序可用SIOCATMARK操作来确定是否有数据剩下。如果在“紧急”(带外)数据前有常规数据,则按序接收这些数据(请注意,recv()和recvfrom()操作不会在一次调用中混淆常规数据与带外数据)。argp指向一个BOOL型数,ioctlsocket()在其中存入返回值。
兼容性:
本函数为Berkeley套接口函数ioctl()的一个子集。其中没有与FIOASYNC等价的命令,SIOCATMARK是套接口层次支持的唯一命令。
返回值:
成功后,ioctlsocket()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
错误代码:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
WSAEINVAL:cmd为非法命令,或者argp所指参数不适用于该cmd命令,或者该命令
不适用于此种类型的套接口。
WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
WSAENOTSOCK:描述字不是一个套接口。
参见:
socket(), setsockopt(), getsockopt(), WSAAsyncSelect().
来自 <https://blog.csdn.net/andylin02/article/details/3184588>