服务端:
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#define ASSERT assert
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <cassert>
#include <list>
using namespace std;
static const int c_iPort = 10001;
bool GraceClose(SOCKET *ps);
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
int main(void)//int __cdecl main(void) 默认的函数调用,不规定参数多少,_stdcall严格控制参数产生
{
WSADATA wsaData;//1.1创建一个名为wsaData 的WSADATA对象。wsaData(使用版本,最高版本....struct结构体)
//调用WSAStartup并将其值返回为整数并检查错误
int iResult=SOCKET_ERROR;//1.1
SOCKET ListenSocket = INVALID_SOCKET;//2.2监听invalid(病人) 为服务器创建一个名为ListenSocket 的SOCKET对象,以侦听客户端连接
SOCKET ClientSocket = INVALID_SOCKET;//5.1创建一个名为ClientSocket 的临时SOCKET对象,以接受来自客户端的连接
struct addrinfo *result = NULL;//2.1
// 1.2初始化 Winsock
ZeroMemory(&wsaData, sizeof(WSADATA));
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//WSAStartup函数启动使用WS2_32.DLL,Winsock的2.2版
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
//建立服务端程序的监听嵌套字
ListenSocket = socket(AF_INET, SOCK_STREAM, 0);//调用套接字函数,并将其值返回到ListenSocket变量
if (ListenSocket == INVALID_SOCKET)//2.4检查是否有错误,以确保该套接字是有效的套接字 INVALID_SOCKET为一个嵌套字对象
{
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
sockaddr_in adrServ;
ZeroMemory(&adrServ, sizeof(sockaddr_in));
adrServ.sin_family = AF_INET;
adrServ.sin_port = htons(c_iPort);
adrServ.sin_addr.s_addr = INADDR_ANY;
// 3.1绑定嵌套字
iResult = bind(ListenSocket, (sockaddr*)&adrServ, sizeof(sockaddr_in));
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());//如果没有错误发生,则 绑定返回零
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);//3.2释放该分配的内存
//4.1监听嵌套字
iResult = listen(ListenSocket, FD_SETSIZE);//backlog参数设置为SOMAXCONN,允许在队列中允许最大合理数量的挂起连接
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 将套接口从阻塞状态设置到非阻塞状态
unsigned long ulMode = 1;
iResult = ioctlsocket(ListenSocket, FIONBIO, &ulMode);
ASSERT(SOCKET_ERROR != iResult);
fd_set fsListen; //fd_set为套字集合
FD_ZERO(&fsListen); //FD_ZERO(*set)将集合set清空
fd_set fsRead;
FD_ZERO(&fsRead);
timeval tv;
tv.tv_sec = 1;//同步 时间计算获得时间延迟 tv_sec 代表多少秒
tv.tv_usec = 0;//tv_usec 代表多少微秒 1000000 微秒 = 1秒
list<SOCKET> sl;
int i = 2;
for (;;)
{
// 接收来自客户端的连接, 并将新建的套接字加入套接字列表中
FD_SET(ListenSocket, &fsListen);//FD_SET(s, *set)将套接字s加入到集合set中
iResult = select(1, &fsListen, NULL, NULL, &tv);//选择监听哪一个I/O
if (iResult > 0)//端口循环
{
sockaddr_in adrClit;
int iLen = sizeof(sockaddr_in);
ZeroMemory(&adrClit, iLen);
SOCKET skAccept = accept(ListenSocket, (sockaddr*)&adrClit, &iLen);
ASSERT(INVALID_SOCKET != skAccept);//断言,存在程序停止,不存在程序继续进行
sl.push_back(skAccept);//回调
cout << "新的连接 " << skAccept << ", c_ip: "
<< inet_ntoa(adrClit.sin_addr) << ", c_port: " << ntohs(adrClit.sin_port) << endl;
}
// 将套接字列表中的套接字加入到可读套接字集合中,以便在可以检测集合中的套接字是否有数据可读
FD_ZERO(&fsRead);//清除
for (list<SOCKET>::iterator iter = sl.begin(); iter != sl.end(); ++iter)//迭代程序
{
FD_SET(*iter, &fsRead);
}
// 检测集合中的套接字是否有数据可读
iResult = select(sl.size(), &fsRead, NULL, NULL, &tv);
if (iResult > 0)
{
for (list<SOCKET>::iterator iter = sl.begin(); iter != sl.end(); ++iter)
{
// 如果有数据可读, 则遍历套接字列表中的所有套接字,检测出有数据可读的套接字
iResult = FD_ISSET(*iter, &fsRead);// FD_ISSET(s, *set)判断套接字s是否在集合中有信号
if (iResult > 0)
{
// 读取套接字上的数据
const int c_iBufLen = 512;
char recvBuf[c_iBufLen + 1] = { '\0' };
int iRecv = SOCKET_ERROR;
iRecv = recv(*iter, recvBuf, c_iBufLen, 0);
if (iRecv <= 0)// 读取出现错误或者对方关闭连接
{
iRecv == 0 ? cout << "Connection shutdown at socket " << *iter << endl ://优雅关闭
cout << "Connection recv error at socket " << *iter << endl;// 粗暴关闭
iResult = GraceClose(&(*iter));//优雅关闭
ASSERT(iResult);
}
else
{
recvBuf[iRecv] = '\0';
cout << "Server recved message from socket " << *iter << ": " << recvBuf << endl;
// 创建可写集合
FD_SET fsWrite;
FD_ZERO(&fsWrite);
FD_SET(*iter, &fsWrite);
// 如果可可写套接字, 则向客户端发送数据
iResult = select(1, NULL, &fsWrite, NULL, &tv);
if (iResult <= 0)
{
int iWrite = SOCKET_ERROR;
iWrite = send(*iter, recvBuf, iRecv, 0);
if (SOCKET_ERROR == iWrite)
{
cout << "Send message error at socket " << *iter << endl;
iResult = GraceClose(&(*iter));
ASSERT(iResult);
}
}
}
}
} //for
sl.remove(INVALID_SOCKET); // 删除无效的套接字, 套接字在关闭后被设置为无效 invalid
} //if
} //for (;;)
// 将套接字设置回阻塞状态
ulMode = 0;
iResult = ioctlsocket(ListenSocket, FIONBIO, &ulMode);
ASSERT(SOCKET_ERROR != iResult);
iResult = GraceClose(&ListenSocket);
ASSERT(iResult);
// shutdown the connection since we're done6.断开服务器
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);//关闭socket
WSACleanup();//WSACleanup函数终止使用Winsock 2 DLL(的WS2_32.DLL)
system("pause");
return 0;
}
bool GraceClose(SOCKET *ps)
{
const int c_iBufLen = 512;
char szBuf[c_iBufLen + 1] = { '\0' };
// 关闭该套接字的连接
int iRet = shutdown(*ps, SD_SEND);
while (recv(*ps, szBuf, c_iBufLen, 0) > 0);
if (SOCKET_ERROR == iRet)
{
return false;
}
// 清理该套接字的资源
iRet = closesocket(*ps);
if (SOCKET_ERROR == iRet)
{
return false;
}
*ps = INVALID_SOCKET;
return true;
}
客户端
#define WIN32_LEAN_AND_MEAN
#define ASSERT assert
#include <iostream>
#include <cassert>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
static const char c_szIP[] = "127.0.0.1";
static const int c_iPort = 10001;
bool GraceClose(SOCKET *ps);
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
//#define DEFAULT_BUFLEN 512
//#define DEFAULT_PORT "27015"
int __cdecl main(int argc, char **argv)
{
WSADATA wsaData;
int iResult = SOCKET_ERROR;
ZeroMemory(&wsaData, sizeof(WSADATA));
// Initialize Winsock初始化winsocket
SOCKET ConnectSocket = INVALID_SOCKET;//1.1创建嵌套字
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//iresult返回的值是多少0
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
struct addrinfo *result = NULL,hints;
// 建立连接套接字
ConnectSocket = socket(AF_INET, SOCK_STREAM, 0);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// 初始化连接套接字地址信息
sockaddr_in adrServ;
ZeroMemory(&adrServ, sizeof(sockaddr_in));
adrServ.sin_family = AF_INET;
adrServ.sin_port = htons(c_iPort);
adrServ.sin_addr.s_addr = inet_addr(c_szIP);
// 将套接口从阻塞状态设置到非阻塞状态
unsigned long ulEnable = 1;
iResult = ioctlsocket(ConnectSocket, FIONBIO, &ulEnable);
ASSERT(SOCKET_ERROR != iResult);
fd_set fsWrite;
TIMEVAL tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
cout << "客户端开始连接到服务器..." << endl;
for (;;)
{
// 使用非阻塞方式连接服务器,请注意connect操作的返回值总是为SOCKET_ERROR
iResult = connect(ConnectSocket, (sockaddr*)&adrServ, sizeof(sockaddr_in)); //sockaddr_in
int iErrorNo = SOCKET_ERROR;
int iLen = sizeof(int);
// 如果getsockopt返回值不为0,则说明有错误出现
if (SOCKET_ERROR == iResult && 0 != getsockopt(ConnectSocket, SOL_SOCKET, SO_ERROR, (char*)&iErrorNo, &iLen))
{
cout << "连接到服务器时出错;错误号是 " << iErrorNo
<< ". 程序现在将退出." << endl;
exit(-1);
}
FD_ZERO(&fsWrite);
FD_SET(ConnectSocket, &fsWrite);
// 如果集合fsWrite中的套接字有信号,则说明连接成功,此时iRet的返回值大于0
iResult = select(1, NULL, &fsWrite, NULL, &tv);
if (iResult > 0)
{
cout << "Successed connect to the server..." << endl;
break;
}
cout << "retrying" << endl;
} //for
for (;;)
{
const int c_iBufLen = 512;
char szBuf[c_iBufLen + 1] = { '\0' };
cout << "向服务器发送消息:";
cin >> szBuf;
if (0 == strcmp("退出", szBuf))
{
break;
}
FD_ZERO(&fsWrite);
FD_SET(ConnectSocket, &fsWrite);
// 如果集合fsWrite中的套接字有信号, 则可以用send操作发数据
iResult = select(1, NULL, &fsWrite, NULL, &tv);
if (0 < iResult)
{
iResult = send(ConnectSocket, szBuf, strlen(szBuf), 0);
if (SOCKET_ERROR == iResult)
{
cout << "send error." << endl;
break;
}
fd_set fsRead;
FD_ZERO(&fsRead);
FD_SET(ConnectSocket, &fsRead);
// 如果集合fsRead中的套接字有信号, 则可以用recv操作读数据
iResult = select(1, &fsRead, NULL, NULL, &tv);
if (0 < iResult)
{
iResult = recv(ConnectSocket, szBuf, c_iBufLen, 0);
if (0 == iResult)
{
cout << "connection shutdown." << endl;
break;
}
else if (SOCKET_ERROR == iResult)
{
cout << "recv error." << endl;
break;
}
szBuf[iResult] = '\0';
cout << szBuf << endl;
}
} //if
} //for
// 将套接字设置回阻塞状态
ulEnable = 0;
iResult = ioctlsocket(ConnectSocket, FIONBIO, &ulEnable);
ASSERT(SOCKET_ERROR != iResult);
// 关闭监听套接字
iResult = GraceClose(&ConnectSocket);
ASSERT(iResult);
// cleanup
closesocket(ConnectSocket);
WSACleanup();
system("pause");
return 0;
}
bool GraceClose(SOCKET *ps)
{
const int c_iBufLen = 512;
char szBuf[c_iBufLen + 1] = { '\0' };
// 关闭该套接字的连接
int iRet = shutdown(*ps, SD_SEND);
while (recv(*ps, szBuf, c_iBufLen, 0) > 0);
if (SOCKET_ERROR == iRet)
{
return false;
}
// 清理该套接字的资源
iRet = closesocket(*ps);
if (SOCKET_ERROR == iRet)
{
return false;
}
*ps = INVALID_SOCKET;
return true;
}