I/O模型 之Select 模型; Select管理TCP server

一:Tcp Serverwindows

1: 當咱們的Server接入一個客戶端進來之後,就要管理好負責從客戶端來接收數據,可是服務器要對多個客戶端從不能一直等待這一個客戶端有數據進來;服務器

2: 咱們的server還要讀寫數據,總不能一直等着有新的用戶進來;socket

3: 須要一種方式來管理, select模型來管理咱們的Tcp Server;性能

1> 建立一個句柄集合,這個集合裏面有要管理的全部的socket(句柄);server

2> 使用select等待這些句柄上面;事件

3> 監聽socket有數據進來了,表示有新的鏈接;ip

4> 已經創建好鏈接的socket,有數據進來了,表示服務器收到了客戶端的數據;同步

5> 若是客戶端關閉了socket或斷網了select也能監測獲得;string

二:Selectit

1: 建立一個監聽socket:

int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (s == INVALID_SOCKET) { return -1; }

struct sockaddr_in sockaddr;

sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

sockaddr.sin_family = AF_INET;

sockaddr.sin_port = htons(port);

int ret = bind(s, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));

ret = listen(s, 1);

2: 配置句柄管理集合:

fd_set socket_set;

FD_ZERO(&socket_set);

FD_SET(s, &socket_set);

FD_CLR(sock, socket_set);

FD_ISSET(s, &socket_set)

int ret = select(0, &socket_set, NULL, NULL, NULL);

三:TCP server 代碼:

// Server.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib,"ws2_32.lib")
#endif
#define MAX_BUF_LEN 4096
static int client_fd[4096];
static int socket_count;
static unsigned char recv_buf[MAX_BUF_LEN];
int main(int argc, char** argv)
{
int ret;
// 配置windows
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2,2);
ret = WSAStartup(wVersionRequested,&wsaData);
if (ret != 0)
{
system("pause");
return -1;
}
#endif
// step1 建立一個監聽socket
int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // TCP
if (s == INVALID_SOCKET)
{
goto failed;
}

// ip 地址+端口
struct sockaddr_in sockaddr;
sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(8080); //127.0.0.1:6080 端口
ret = bind(s, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
if (ret != 0)
goto failed;

// 開始監聽
ret = listen(s,1);
fd_set set; // 定義一個句柄集合
while (1)
{
// 清空一下句柄語句
FD_ZERO(&set);
FD_SET(s,&set);// 將咱們監聽的句柄加入到等待的集合上

// 其餘客戶端進入來的socket加入到咱們的句柄集合
for (int j = 0; j < socket_count;j++)
{
FD_SET(client_fd[j],&set);
}
ret = select(0,&set,NULL,NULL,NULL);
if (ret < 0)
{
printf("select error\n");
}
else if (ret == 0)
{
printf("select timeout\n");
continue;
}
// 判斷是哪一個句柄發生了事件
if (FD_ISSET(s, &set)) // 發送過來的是鏈接請求
{
// 等待客戶端接入進來
struct sockaddr_in c_address; //客戶端IP地址
int address_len = sizeof(c_address);
// cs 服務端爲客戶端建立的配對的socket;
// c_address 客戶端的IP地址和端口
printf("waitting...\n");
int cs = accept(s, (struct sockaddr*) &c_address, &address_len);
printf("new client %s :%d\n", inet_ntoa(c_address.sin_addr));
client_fd[socket_count] = cs;
socket_count++;
continue;
}

for (int j = 0; j < socket_count; j++)
{
if (FD_ISSET(client_fd[j], &set)&& client_fd[j] != INVALID_SOCKET)
{
int len = recv(client_fd[j], (char*)recv_buf, MAX_BUF_LEN, 0);
if (len <= 0)
{
closesocket(client_fd[j]);
client_fd[j] = INVALID_SOCKET;
}
else
{
recv_buf[len] = 0;
printf("recv:%s\n", recv_buf);
send(client_fd[j], (char*)recv_buf,len,0);
}
}
}

}
failed:
if (s != INVALID_SOCKET)
{
closesocket(s);
}

#ifdef WIN32
WSACleanup();
#endif
return 0;
}

TCP Client 代碼:

#include "stdafx.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib,"WSOCK32.LIB")
#endif


int main()
{
int ret;
// 配置windows
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
ret = WSAStartup(wVersionRequested, &wsaData);
if (ret != 0)
{
system("pause");
return -1;
}
#endif
// step1 建立一個監聽socket
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // TCP
if (s == INVALID_SOCKET)
{
goto failed;
}

// ip 地址+端口
struct sockaddr_in sockaddr;
sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(8080); //127.0.0.1:6080 端口
// 發送鏈接請求到服務的的監聽socket
ret = connect(s, ((struct sockaddr*) &sockaddr),sizeof(sockaddr));

char buf[11];
memset(buf,0,11);
send(s,"Hello",5,0);
recv(s,buf,5,0);
printf("%s\n",buf);
if (ret != 0)
goto failed;
failed:
if (s != INVALID_SOCKET)
{
closesocket(s);
s = INVALID_SOCKET;
}

#ifdef WIN32
WSACleanup();
#endif

return 0;
}

 四:Select缺點

1: 性能很差,每次有事件的時候都要遍歷全部的句柄,而後查是哪一個句柄的事件;

2: 可以管理的句柄的數目是有限制的, 2048個

3: socket的讀和寫仍然是同步的, 咱們發送和接受數據的時候會等在網卡上面;

相關文章
相關標籤/搜索