知识点:
- #define WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include<Windows.h>
#include<WinSock2.h>
// 作用:"#define WIN32_LEAN_AND_MEAN"尽量避免引用windows.h中早期的定义的宏
- 配置一个清爽的开发目录
项目 属性>常规 选中所有配置、所有平台中的输出目录、中间目录
值为:
$(SolutionDir)../bin/$(Platform)/$(Configuration)
$(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)
- 引入ws2_32.lib的静态链接库
方法一:
windows平台下使用宏:
#pragma comment(lib,"ws2_32.lib")//缺点:不能跨平台
方法二:
在项目属性中 链接器>输入>附加依赖项 中进行添加即可,为一般做法、推荐使用
代码进度
- helloSocket
// Test.cpp
#define WIN32_LEAN_AND_MEAN
#include<Windows.h>
#include<WinSock2.h>
// #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用)
int main()
{
WORD ver = MAKEWORD(2,2);
WSADATA dat;
WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了windows的动态库
///
///
WSACleanup();// 关闭
return 0;
}
注:以上代码目前基本不需要理解,为windows环境下socket配置的固定操作。
-
EasyTcpServer
简易的可以接收客户端的socket。
注意事项:inet_addr是老函数,高版本VS在编译时默认使用了新函数,所以会报该错误。参考文章:
https://blog.csdn.net/gongxun1994/article/details/83418268也可以通过错误提示中的定义宏的方式来解决
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include<Windows.h> #include<WinSock2.h> #include<stdio.h> // #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用) int main() { WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了 windows的动态库 /// // 1 创建一个socket SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 2 绑定端口 // SOCKET_ERROR sockaddr_in _addr = {}; _addr.sin_family = AF_INET; _addr.sin_port = htons(3456);// host to net unsigned short _addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // inet_addr("127.0.0.1"); // INADDR_ANY;// htons() //注:使用 inet_addr("127.0.0.1")报错。// inet_addr("127.0.0.1")太老 if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_addr, sizeof(sockaddr_in))) { printf("Error,绑定端口失败\n"); } else { printf("绑定端口成功\n"); } // 3 监听网络端口 if (SOCKET_ERROR == listen(_sock, 5)) { printf("Error,监听网络端口失败"); } else { printf("监听网络端口成功"); } // 4 等待接收 sockaddr_in _clientAddr = {}; int nAddrLen = sizeof(sockaddr_in); SOCKET _cSock = INVALID_SOCKET;// 定义一个无效的socket char msgBuf[] = "Hello, I'm Server."; while (true) { _cSock = accept(_sock, (sockaddr*)&_clientAddr, &nAddrLen);// 第三个是传入结构的长度 if (INVALID_SOCKET == _cSock)// 大规模的链接中可能出现无效的socket链接,一般的小程序基本不会出现 { printf("错误,接收到无效的客户端socket...\n"); } printf("新客户端加入:IP = %s \n", inet_ntoa(_clientAddr.sin_addr)); // 5 向客户端发送一条数据 send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);// strlen(msgBuf)+1 把结尾符一并发出去,因为计算长度的时候不会计算结尾符 ???啥意思 } // 6 关闭 closesocket(_sock); /// // 清除windows socket环境 WSACleanup(); return 0; }
-
EasyTcpClient
代码如下
//client.cpp
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS // 或者将sdl选择关闭
#include<Windows.h>
#include<WinSock2.h>
#include<stdio.h>
// #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用)
int main()
{
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了windows的动态库
///
// 1 建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET==_sock)
{
printf("Error,建立Socket失败\n");
}
else
{
printf("建立Socket成功...\n");
}
// 2 连接服务器
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;// ipv4
_sin.sin_port = htons(3456);
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
if (SOCKET_ERROR == ret)
{
printf("Error,连接服务端失败\n");
}
else
{
printf("连接服务端成功...\n");
}
// 3 接收服务器信息 recv
char recvBuf[256] = {};
int nlen= recv(_sock, recvBuf, 256, 0); // 返回数据的长度
if (nlen>0)
{
printf("接收到的数据:%s\n", recvBuf);
}
// 4 关闭Socket
closesocket(_sock);
///
WSACleanup();// 关闭
getchar();
return 0;
}
- 使用SVN管理我们的项目
建立良好的代码管理习惯 - 使用struct来传输数据
略 - 使用网络数据报文来传输数据
定义:
网络数据报文有两个部分,包头和包体,是网络消息的基本单元
包头:描述本次消息包的大小,描述数据的作用
包体:数据 - 将服务端升级到select模型
select参数:
select(
_In_ int nfds,
_Inout_opt_ fd_set FAR * readfds,
_Inout_opt_ fd_set FAR * writefds,
_Inout_opt_ fd_set FAR * exceptfds,
_In_opt_ const struct timeval FAR * timeout
);
/*
nfds:在windows平台下无意义,但是在Linux、MacOS、Unix下有意义
_In:只能被传入不能被改变
*/
---------------------------------------------------
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
/*
默认FD_SETSIZE为64
*/
---------------------------------------------------
#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
---------------------------------------------------
if (FD_ISSET(_sock, &fdRead))// 是否有_sock可读
{
}
---------------------------------------------------
// 相关的一些操作
// 伯克利 socket ??不知道啥是伯克利 socket
fd_set fdRead;
FD_ZERO(&fdRead);// 清空
FD_SET(_sock, &fdRead);// 设置
FD_CLR(_sock, &fdRead);// 清除
timeval t = {10,0}; // 最大阻塞时间
int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, &t);
- 为client添加线程 c++标准线程库
#include<thread>
void cmdThread()
{
char cmdBuf[256]={};
scanf("%s",cmdBuf);
if(0==strcmp(cmdBuf,"exit"))
{
// 略
}
}
int main()
{
// 启动线程
std::thread t1(cmdThread,_sock);
t1.detach(); // 与主线程分离
}