目錄
- 基於Select模型的通訊模擬--win32程式設計程式碼
- 程式設計環境
- 服務端
- 客戶端
- 相關連結
- 基於Select模型的通訊模擬(詳解)
基於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;
}
相關連結
基於Select模型的通訊模擬(詳解)
https://www.cnblogs.com/wa2211lq/p/18553178