这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战
服务器端:
1、包含网络头文件网络库
/*
* 基于Windows的网络编程协议库
* 并链接到Ws2_32.lib库
*/
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
2、打开网络库
/*
* 1. 打开网络库
*/
WSADATA wdScoket;
int nRes = WSAStartup(wdVersion, &wdScoket);
if (nRes) {
switch (nRes) {
case WSASYSNOTREADY:
{
printf("错误代码:%d\n", WSASYSNOTREADY);
printf("原因:底层网络子系统尚未准备好进行网络通信。\n");
printf("解决方案:重启电脑,并检查库文件是否存在\n");
break;
}
case WSAVERNOTSUPPORTED:
{
printf("错误代码:%d\n", WSAVERNOTSUPPORTED);
printf("原因:此特定Windows套接字实现不提供所请求的Windows套接字支持的版本。\n");
printf("解决方案:指定版本不支持,切换换低版本\n");
break;
}
case WSAEINPROGRESS:
{
printf("错误代码:%d\n", WSAEINPROGRESS);
printf("原因:正在阻止Windows Sockets 1.1操作。\n");
printf("解决方案:重启电脑。\n");
break;
}
case WSAEPROCLIM:
{
printf("错误代码:%d\n", WSAEPROCLIM);
printf("原因:已达到对Windows套接字实现支持的任务数量的限制。\n");
printf("解决方案:网络连接达到上限或阻塞,关闭不必要软件。\n");
break;
}
case WSAEFAULT:
{
printf("错误代码:%d\n", WSAEFAULT);
printf("原因:IpWSAData参数不是有效指针。\n");
printf("解决方案:检查参数错误\n");
break;
}
return 0;
}
}
printf("打开网络库成功!\n");
3、校验版本
/*
* 3. 校验版本
*/
if (HIBYTE(wdScoket.wVersion) != 2 || LOBYTE(wdScoket.wVersion) != 2) {
printf("版本有问题!\n");
WSACleanup();
return 0;
}
printf("版本校验成功!\n");
4、创建SOCKET
/*
* 4. 创建socket
*/
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketServer == INVALID_SOCKET) {
int err = WSAGetLastError();
printf("服务器创建socket失败!错误码为: %d\n",err);
WSACleanup();
return 0;
}
printf("服务器创建socket成功!\n");
5、绑定地址与端口
/*
* 5. 绑定地址与端口
*/
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*) & si, sizeof(si))) {
int err = WSAGetLastError();
printf("服务器bind失败,错误码为: %d\n", err);
closesocket(socketServer);
WSACleanup();
return 0;
}
printf("服务器bind成功!\n");
6、开始监听
/*
* 6. 开始监听
*/
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)) {
int err = WSAGetLastError();
printf("服务器liten失败,错误码为: %d\n");
closesocket(socketServer);
WSACleanup();
return 0;
}
printf("服务器listen成功!\n");
7、SELECT(重点)
7.1将服务器句柄装入fd_set
//把集合清零
FD_ZERO(&allSockets);
//装入服务器端
FD_SET(socketServer,&allSockets);
7.2开始轮询循环
//防止轮询数组被传址引用清空
fd_set recvSockets = allSockets;
fd_set sendSockets = allSockets;
fd_set errorSockets = allSockets;
//服务器不会给自己发消息,把服务器从sensockets中移除
FD_CLR(socketServer, &sendSockets);
//设置每次等待时间
struct timeval st;
st.tv_sec = 3;//3秒
st.tv_usec = 3;//3微秒
7.2.1使用select查询有信号的socket句柄
//开始select
int nRes = select(0, &recvSockets, &sendSockets, &errorSockets, &st);
7.2.2没有socket句柄由信号则重新查询
if (nRes == 0) {//没有socket响应
continue;
}
7.2.3select出错则进行错误处理
else {//出现异常
int err = WSAGetLastError();
printf("服务器select失败!错误码为: %d\n", err);
break;
}
7.2.4处理有信号的socket句柄
7.2.4.1处理有读请求的句柄
如果是服务器端:accept后加入fd_set
//循环处理有读请求的句柄
for (u_int i = 0; i < recvSockets.fd_count; i++) {
//如果是服务器句柄有请求,表明有新的客户端要进行accept
if (recvSockets.fd_array[i] == socketServer) {
SOCKET socketClient = accept(socketServer, NULL, NULL);
//accept新客户端失败
if (socketClient == INVALID_SOCKET) {
int err = WSAGetLastError();
printf("accept新的客户端失败,错误码为: %d\n", err);
continue;
}
//accept新客户端成功
FD_SET(socketClient, &allSockets);
printf("服务器获取客户端句柄成功,fd_set中共有%d个句柄!\n", allSockets.fd_count);
}
如果是客户端:recv信息,并根据结果判断是否有recv异常
//如果是客户端,就进行recv
else {
char strBuff[1500] = { 0 };
int nRecv = recv(recvSockets.fd_array[i],strBuff,1500,0);
if (nRecv == 0) {
//客户端正常掉线
SOCKET socketTemp = recvSockets.fd_array[i];
FD_CLR(recvSockets.fd_array[i], &allSockets);
closesocket(socketTemp);
printf("客户端已掉线,fd_set中共有%d个句柄!\n", allSockets.fd_count);
}
//接收到消息就打印出来
else if(nRecv > 0){
printf("客户端的消息是:%s\n", strBuff);
}
//如果遇到错误
else {
int err = WSAGetLastError();
switch (err) {
case 10053:
case 10054:
SOCKET socketTemp = recvSockets.fd_array[i];
FD_CLR(recvSockets.fd_array[i], &allSockets);
closesocket(socketTemp);
printf("客户端已掉线,fd_set中共有%d个句柄!\n", allSockets.fd_count);
}
}
7.2.4.2处理有写请求的句柄
//循环处理有写请求的句柄
for (u_int i = 0; i < sendSockets.fd_count; i++) {
if (send(sendSockets.fd_array[i], "hello", 5, 0) == SOCKET_ERROR) {
int err = WSAGetLastError();
}
}
7.2.4.3处理有异常请求的句柄
//循环处理有错误的句柄
for (u_int i = 0; i < errorSockets.fd_count; i++) {
char str[100] = { 0 };
int len = 0;
if (getsockopt(errorSockets.fd_array[i],SOL_SOCKET,SO_ERROR,str,&len) == SOCKET_ERROR) {
printf("getsocketopt无法获取相关信息!\n");
int getopterr = WSAGetLastError();
printf("服务器getsocketopt失败,错误码为: %d\n", getopterr);
}
//打印getsockopt错误信息
printf("%s\n", str);
}