一:Tcp Server
1: 当我们的Server接入一个客户端进来以后,就要管理好负责从客户端来接收数据,但是服务器要对多个客户端从不能一直等待这一个客户端有数据进来;
2: 我们的server还要读写数据,总不能一直等着有新的用户进来;
3: 需要一种方式来管理, select模型来管理我们的Tcp Server;
1> 创建一个句柄集合,这个集合里面有要管理的所有的socket(句柄);
2> 使用select等待这些句柄上面;
3> 监听socket有数据进来了,表示有新的连接;
4> 已经建立好连接的socket,有数据进来了,表示服务器收到了客户端的数据;
5> 如果客户端关闭了socket或断网了select也能监测得到;
二:Select
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
");
}
else if (ret == 0)
{
printf("select timeout
");
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...
");
int cs = accept(s, (struct sockaddr*) &c_address, &address_len);
printf("new client %s :%d
", 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
", 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
",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的读和写仍然是同步的, 我们发送和接受数据的时候会等在网卡上面;