基于Select模型的通信仿真--win32编程代码
基于Select模型的通信仿真--win32编程代码
编程环境
Visual C++ 6.0
服务端
#include<stdio.h>
#include<string.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
AllocConsole();
freopen("CONOUT$","w",stdout);
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stderr);
// 初始化 Winsock
WSADATA wsaData;
int result=WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup failed with error: %d\n", result);
return 1;
}
//1.创建socket套接字
/*
SOCKET socket(
_In_ int af, //协议地址簇 IPv4:AF_INET/IPv6:AF_INET6
_In_ int type, //类型 流式协议:SOCK_STREAM/数据报协议:SOCK_DGRAM
_In_ int protocol //保护协议 上层协议tcp、udp不用写,填0即可
);
*/
SOCKET listen_socket=socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET==listen_socket)
{
printf("create listen socket failed!!! errcode: %d\n", GetLastError());
WSACleanup();
return -1;
}
//2.给socket绑定端口号
/*
* ip地址结构体
struct sockaddr_in {
ADDRESS_FAMILY sin_family; //协议地址簇
USHORT sin_port; //端口号 2字节
IN_ADDR sin_addr; //ip地址 4字节
CHAR sin_zero[8]; //保留字节 8字节
};
struct sockaddr {
u_short sa_family; //协议地址簇
CHAR sa_data[14]; //14字节
}
*/
struct sockaddr_in local = {0};
local.sin_family = AF_INET;
local.sin_port = htons(8080);//绑定端口 大小端转化:中间设备使用大端序(路由器),但是电脑使用的是小端序,所以要转化
/*
电脑上有多个网卡,服务器可以选择接收哪个或哪些网卡传过来的数据。
如:127.0.0.1(本地环回),则服务器只能和自己通信 INADDR_LOOPBACK
一般写全0地址(0.0.0.0),表示接收全部网卡的数据 INADDR_ANY
*/
//local.sin_addr.s_addr = htonl(INADDR_ANY);//接收全部网卡的数据 大小端转化
local.sin_addr.s_addr = inet_addr("0.0.0.0");//接收全部网卡的数据 字符串ip转成整数ip
/*
int bind(
SOCKET s, //绑定的套接字
const struct sockaddr FAR * name, //ip地址结构体
int namelen //ip地址结构体的长度
);
*/
bind(listen_socket,(struct sockaddr*)&local,sizeof(local));
//3.给socke开启监听属性,只用来接收连接
/*
int listen(
_In_ SOCKET s, //监听套接字
_In_ int backlog //
);
*/
if (-1 == listen(listen_socket, 10))
{
printf("start listen failed!!! errcode: %d\n", GetLastError());
WSACleanup();
return -1;
}
fd_set redset;
//4、初始化文件描述符集合
FD_ZERO(&redset);
//5、添加要检测的监听文件描述符
FD_SET(listen_socket,&redset);
printf("This is SERVER!\n");
while (1)
{
fd_set tmp = redset;
/* 6、不停地检测文件描述符
6.1 超时 select() = 0 再次检测或关闭套接字
6.2 异常 select() = -1 异常处理
6.3 成功 selct() >0
*/
printf("selecting...\n");
int ret = select(-1, &tmp, NULL, NULL, NULL);
if (ret <= 0)
{
printf("select failed!!! errcode: %d\n", GetLastError());
closesocket(listen_socket);
WSACleanup();
return -1;
}
// printf("select = %d\n",ret);
//7、判断文件描述符属于哪一类
//通过将原来redset集合中的文件描述符与select处理过的tmp集合比较,判断哪些文件描述符就绪,如果就绪,是哪一类
for (int i = 0; i < (int)redset.fd_count; i++)
{
if (FD_ISSET(redset.fd_array[i], &tmp))//判断文件描述符(套接字)i的读缓冲区是否有数据
{
//就绪文件描述符是监听描述符
if (redset.fd_array[i] == listen_socket) // 监听套接字接收到新连接
{
if (redset.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
//接收客户端的连接请求
SOCKET client_socket = ::accept(listen_socket, (SOCKADDR*)&addrRemote, &nAddrLen);
FD_SET(client_socket, &redset);
printf("与主机 %s 建立连接\n", inet_ntoa(addrRemote.sin_addr));
}
else
{
printf("Too much connections!\n");
continue;
}
}
else//就绪文件描述符不是监听描述符,是通信描述符
{
//接收信息
char rbuffer[1024] = { 0 };
int len = recv(redset.fd_array[i], rbuffer, 1024, 0);
if (len <= 0)
{
printf("The client %d has disconnected.\n", i);
FD_CLR(redset.fd_array[i], &redset);
shutdown(redset.fd_array[i], SD_BOTH);
closesocket(redset.fd_array[i]);
break;
}
printf("recive from client%d:\t%s\n", i, rbuffer);
//发送信息
char sbuffer[1024] = { 0 };
// 检查接收到的消息
if (strcmp(rbuffer, "计算从1到100的奇数和") == 0)
{
int sum = 0;
for (int j = 1; j <= 100; j += 2)
{
sum += j;
}
printf("send to client%d:\t1到100的奇数和是 %d\n", i, sum);
sprintf(sbuffer, "1到100的奇数和是 %d\n", sum);
}
else
{
printf("send to client%d:\tunknow!\n", i);
sprintf(sbuffer, "unknow!");
}
len = send(redset.fd_array[i], sbuffer, strlen(sbuffer), 0);
if (len == -1)
{
perror("send error");
exit(1);
}
}
}
}
}
closesocket(listen_socket);
FreeConsole();
// 清理 Winsock
WSACleanup();
return 0;
}
客户端
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
AllocConsole();
freopen("CONOUT$","w",stdout);
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stderr);
// 初始化 Winsock
WSADATA wsaData;
int result=WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup failed with error: %d\n", result);
return 1;
}
//1.创建socket套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == client_socket)
{
printf("create socket failed!!! errcode: %d\n", GetLastError());
WSACleanup();
return -1;
}
//2.连接服务器
struct sockaddr_in target;//目标服务器的ip结构体
target.sin_family = AF_INET;
target.sin_port = htons(8080);
target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
{
printf("connect server failed!!!\n");
shutdown(client_socket, SD_BOTH);
closesocket(client_socket);
WSACleanup();
return -1;
}
//3.开始通讯
printf("This is Cilent1.\n\n");
while (1)
{
//发送信息
printf("send:\t");
char sbuffer[1024] = { 0 };
scanf("%s", sbuffer);
send(client_socket, sbuffer, strlen(sbuffer),0);
//接收消息
char rbuffer[1024] = { 0 };
int ret=recv(client_socket, rbuffer, 1024, 0);
if (ret <= 0)
{
break;//断开连接
}
printf("recive:\t%s\n", rbuffer);
}
//4.关闭连接
shutdown(client_socket, SD_BOTH);shutdown(client_socket, SD_BOTH);
closesocket(client_socket);
FreeConsole();
// 清理 Winsock
WSACleanup();
return 0;
}