Linux网络编程select模型的实现
2015-07-28 10:50
666 查看
简单的使用linux下的select模型实现了一个http的server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define LISTENQ 5
#define OPEN_MAX 1024
#define SERV_PORT 10088
#define MAX_LINE 1024
#define INFTIM -1
#define MAXEVENTS 1000
char szHtmlBuf[] = "HTTP/1.1 200 OK\r\n"
"Date: Sat, 05 Jan 2013 03:13:29 GMT\r\n"
"Vary: Accept-Encoding\r\n"
"Content-Type: text/html; charset=gb2312\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 57\r\n"
"\r\n"
"<html> <head>欢迎光临</head> <body>屌丝逆袭季</body></html>";
fd_set fds;
void echo_srv(int clientFd)
{
//处理用户请求数据
char line[MAX_LINE];
printf( "开始读取数据");
int n = read(clientFd, line, sizeof(line));
if(n < 0)
{
//#define ECONNRESET 104 /* Connection reset by peer */
if(errno == ECONNRESET)
{
close(clientFd);
FD_CLR(clientFd, &fds);
printf("异常退出\n");
}
else
{
printf("网络异常");
exit(-1);
}
}
else if(n == 0)
{
close(clientFd);
FD_CLR(clientFd, &fds);
printf("正常退出\n");
}
else
{
line
= 0;
printf("接收到数据:%s\n", line);
write(clientFd, szHtmlBuf, sizeof(szHtmlBuf));
}
}
int main()
{
struct sockaddr_in cliaddr, servaddr;
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
if( listenFd < 0)
{
printf("socket函数执行失败");
return 1;
}
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_aton('10.132.10.64', &(servaddr.sin_addr));
//servaddr.sin_addr.s_addr = inet_addr("10.132.10.64");
servaddr.sin_port = htons(SERV_PORT);
if(bind(listenFd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("bind函数执行失败");
return 1;
}
if(listen(listenFd, LISTENQ) < 0)
{
printf("listen函数执行失败");
return 1;
}
printf("listen函数执行成功\n");
//select 部分
int maxfd;
FD_ZERO(&fds);
do{
FD_SET(listenFd, &fds);
maxfd = listenFd + 1;
int nRead = 0;
if( (nRead = select(maxfd + 1, &fds, NULL, NULL, NULL)) < 0)
{
printf("select 失败\n");
exit(-1);
}
printf("select find data Change\n");
for(int i = 0; i <= maxfd && nRead > 0; i++)
{
if(!FD_ISSET(i, &fds))
{
continue;
}
--nRead;
if(i == listenFd)
{
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenFd, (struct sockaddr*)&cliaddr, &clilen);
if(connfd < 0)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
{
printf( "fail to accept new client \n");
continue;
}
}
printf("Ip: %s 到此一游\n", inet_ntoa(cliaddr.sin_addr));
FD_SET(connfd, &fds);
maxfd = (connfd > maxfd ? connfd : maxfd);
}
else
{
echo_srv(i);
}
}
}while(true);
return 0;
}
直接用浏览器进行测试即可:http://服务器Ip地址:10088
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2014-01/94777.htm
在网络上看到太多关于select i/o用法,都不是非常的详细。
经过摸索,把我实现的例子张贴上来,供大家参考。
描述:
服务器:实现并发tcp服务器,最多允许64 个客户端连接。
客户端:与服务器建立tcp连接,发送数据给服务器。服务器接收数据。
服务器代码:(红色为我的代码)
// SelectServerDlg.h : header file
//
#if !defined(AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_)
#define AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CSelectServerDlg dialog
UINT proc(LPVOID lParam);
class CSelectServerDlg : public CDialog
{
// Construction
public:
CSelectServerDlg(CWnd* pParent = NULL); // standard constructor
CWinThread *pThread;
BOOL bRun;
// Dialog Data
//{{AFX_DATA(CSelectServerDlg)
enum { IDD = IDD_SELECTSERVER_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSelectServerDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CSelectServerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_)
————————————————————————————————————————
//SelectServerDlg.cpp代码
BOOL CSelectServerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
WSAData wsadata;
WORD wVersionRequest;
wVersionRequest=MAKEWORD(2,2);
if(WSAStartup(wVersionRequest,&wsadata)!=0)
TRACE("没有DLL可用\n");
if(LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2)
{
AfxMessageBox("socket dll 启动失败");
WSACleanup();
}
pThread = AfxBeginThread(proc, this);
return TRUE; // return TRUE unless you set the focus to a control
}
——————————————————————————————————————
//服务器线程
UINT proc(LPVOID lParam)
{
try{
fd_set rset;
CSelectServerDlg *pDlg = (CSelectServerDlg*)lParam;
struct sockaddr_in seraddr,cliaddr;
int client[FD_SETSIZE],listensock;
FD_ZERO(&rset);
seraddr.sin_addr.s_addr = inet_addr("192.168.1.24");
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(6500);
memset(seraddr.sin_zero, 0, 8);
listensock = socket(AF_INET, SOCK_STREAM, 0);
TRACE("listen socket :%d\n", listensock);
bind(listensock, (struct sockaddr *)&seraddr, sizeof(seraddr));
listen(listensock, 20);
for(int i = 0; i<FD_SETSIZE; i++)
client[i] = -1;
pDlg->bRun = TRUE;
int nReady;
struct timeval timeout;
int isset;
int err;
int maxfd;
int connfd;
int clilen;
maxfd = listensock;
char buf[1024];
int recvbytes = 0;
memset(buf, 0, 1024);
while(pDlg->bRun){
Sleep(1);
timeout.tv_sec = 0;
timeout.tv_usec = 10;
/*
listen socket 加入fdset
*/
FD_SET(listensock, &rset);
for(int n = 0; n<FD_SETSIZE; n++)
{
if(client
>0){
FD_SET(client
, &rset);
if(client
> maxfd)
maxfd = client
;
}
}
nReady = select(maxfd+1, &rset, NULL, NULL, &timeout);
if(nReady == 0)
//TRACE("select fun time out\n");
;
else if(nReady == -1)
{
TRACE("socket error\n");
err = WSAGetLastError();
switch(err)
{
case WSANOTINITIALISED:
TRACE("WSANOTINITIALISED 未初始化\n");
break;
case WSAEFAULT:
TRACE("WSAEFAULT\n");
break;
case WSAENETDOWN:
TRACE("WSAENETDOWN\n");
break;
case WSAEINVAL:
TRACE("WSAEINVAL\n");
break;
case WSAEINTR:
TRACE("WSAEINTR\n");
break;
case WSAEINPROGRESS:
TRACE("WSAEINPROGRESS\n");
break;
case WSAENOTSOCK:
TRACE("WSAENOTSOCK\n");
break;
}
}
else
{
/*
测试已经建立连接的socket
*/
unsigned long ul = 1;
for(int j = 0; j<FD_SETSIZE; j++)
{
if(client[j]>0)
{
memset(buf,0,1024);
isset = FD_ISSET(client[j],&rset);
if(isset)
{
//ioctlsocket(client[j], FIONBIO, &ul);
recvbytes = recv(client[j], buf, 1024, NULL);
if(recvbytes>0)
TRACE("from socket: %d,接收字节数:%d,%s\n",client[j], recvbytes, buf);
else if(recvbytes == 0)
{
closesocket(client[j]);
TRACE("client socket :%d closed! \n",client[j]);
client[j] = -1;
}
}
}
}
/*
测试是否有连接?
*/
isset = FD_ISSET(listensock, &rset);
FD_ZERO(&rset);
if(isset)
{
TRACE("conncetion.... \n");
clilen = sizeof(cliaddr);
connfd = accept(listensock,(struct sockaddr *)&cliaddr, &clilen);
TRACE("connection comes from ip:%d.%d.%d.%d\n",cliaddr.sin_addr.S_un.S_un_b.s_b1,
cliaddr.sin_addr.S_un.S_un_b.s_b2,
cliaddr.sin_addr.S_un.S_un_b.s_b3,
cliaddr.sin_addr.S_un.S_un_b.s_b4);
for(int s=0; s<FD_SETSIZE; s++){
if(client[s]<0){
client[s] = connfd;
break;
}
}
TRACE("con number:%d\n", s+1);
TRACE("socket id:");
for(int k = 0; k < FD_SETSIZE; k++)
{
if(client[k]>0)
TRACE("%d,",client[k]);
}
TRACE("\n");
}
}
}
}
catch (...) {
TRACE("exception.........!!\n");
}
return TRUE;
}
——————————————————————————————————
//客户端代码:
BOOL CTcpClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
WSAData wsadata;
WORD wVersionRequest;
wVersionRequest=MAKEWORD(2,2);
if(WSAStartup(wVersionRequest,&wsadata)!=0)
TRACE("没有DLL可用\n");
if(LOBYTE(wsadata.wVersion)!=2||HIBYTE(wsadata.wVersion)!=2)
{
AfxMessageBox("socket dll 启动失败");
WSACleanup();
}
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(6500);
serveraddr.sin_addr.S_un.S_un_b.s_b1=192;
serveraddr.sin_addr.S_un.S_un_b.s_b2=168;
serveraddr.sin_addr.S_un.S_un_b.s_b3=1;
serveraddr.sin_addr.S_un.S_un_b.s_b4=24;
clientsocket=socket(AF_INET,SOCK_STREAM,0);
return TRUE; // return TRUE unless you set the focus to a control
}
void CTcpClientDlg::OnSend()
{
// TODO: Add your control notification handler code here
nResult=connect(clientsocket,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("连接成功\n");
char *szSend="123456789";
nResult=sendto(clientsocket,szSend,9,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("发送成功\n");
}
}
}
void CTcpClientDlg::OnButton2()
{
// TODO: Add your control notification handler code here
closesocket(clientsocket);
}
void CTcpClientDlg::OnButton3()
{
// TODO: Add your control notification handler code here
char *szSend="123456789";
nResult=sendto(clientsocket,szSend,9,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("发送成功\n");
}
}
(完)
在很长的时间内,相比于Windows平台众多的选择,select模式都是linux平台下网络通信的不多选择之一(在epoll未出生之前)。这种模式在面对大量短连接的时候,比OCOT有高的效率,可以避免频繁的上下文切换。
select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到遇到以下情况之一select会退出:
(1) 监控的句柄至少有一个发生变化,返回值大于1。
(2) 等待时间已到,返回值等于0。
(3) 收到信号退出,返回值小于0。
select操作的内部会在指定的时间内,不断的检测受控集合中的套接字是否可读,可写或有错误,在select返回时只保留发生状态变化的套接字。通过判断select的返回值,就可以了解该调用是否有文件描述符可读或是select调用退出等信息。
要想让程序运行,光有一个select的调用不是不够的,我们还需要一系列的配套数据结构和算法来帮助将套接字归类,判断是否有指定的套接字等等,结构体fd_set和宏FD_ZERO,FD_CLR,FD_SET,FD_ISSET等就是我们所需要的。通过定义一个fd_set的对象,例如可读集合,有相同需求的套接字都可以放置到这个对象中,可以通过FD_SET这个宏来完成,当然在这之间需要利用FD_ZERO将这个对象初始化。等完成了以上操作后,便会调用select查询是否有套接字可读。等待select完成后,如果有套接字可读,就可以利用FD_ISSET判断是否为监听套接字,转而执行accept操作。如果一个套接字已经关闭,就可以利用FD_CLR将其从fd_set对象中剔除。
服务器端源代码:
1
#include <iostream>
2
#include <sys/socket.h>
3
#include <sys/types.h>
4
#include <arpa/inet.h>
5
#include <sys/select.h>
6
#include <sys/ioctl.h>
7
using namespace std;
8
9
int main()
10
{
11
12
int server_sock = socket(AF_INET,SOCK_STREAM,0);
13
if(server_sock < 0)
14
{
15
cout << "create socket error" << endl;
16
return -1;
17
}
18
19
struct sockaddr_in server_addr;
20
memset(&server_addr,0,sizeof(server_addr));
21
server_addr.sin_family = AF_INET;
22
server_addr.sin_port = htons(5555);
23
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
24
25
if(bind(server_sock,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
26
{
27
cout << "bind socket error" << endl;
28
close(server_sock);
29
return -1;
30
}
31
32
listen(server_sock,10);
33
34
fd_set read_set;
35
fd_set test_set;
36
FD_ZERO(&read_set);
37
FD_SET(server_sock,&read_set);
38
39
struct timeval tm;
40
tm.tv_sec = 5;
41
tm.tv_usec = 500;
42
43
44
while(true)
45
{
46
int nread = 0;
47
test_set = read_set;
48
int ret = select(FD_SETSIZE,&test_set,NULL,NULL,&tm);
49
50
if(ret < 0)
51
{
52
cout << "select error" << endl;
53
close(server_sock);
54
return -1;
55
}
56
else if(ret == 0)
57
{
58
//cout << "waitout" << endl;
59
continue;
60
}
61
else
62
{
63
for(int fd=0; fd<FD_SETSIZE; ++fd)
64
{
65
if(FD_ISSET(fd,&test_set))
66
{
67
//如果有新的连接到达
68
if(fd == server_sock)
69
{
70
int sock = accept(server_sock,NULL,NULL);
71
FD_SET(sock,&read_set);
72
}
73
else
74
{
75
ioctl(fd,FIONREAD,&nread);
76
77
if(nread == 0) //客户端已经关闭
78
{
79
close(fd);
80
FD_CLR(fd,&read_set);
81
cout << "client has removed " << fd << endl;
82
}
83
else
84
{
85
char buf[128];
86
recv(fd,buf,128,0);
87
cout << buf << endl;
88
send(fd,buf,strlen(buf)+1,0);
89
}
90
}
91
92
}
93
94
}
95
96
}
97
98
}
99
}
100
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define LISTENQ 5
#define OPEN_MAX 1024
#define SERV_PORT 10088
#define MAX_LINE 1024
#define INFTIM -1
#define MAXEVENTS 1000
char szHtmlBuf[] = "HTTP/1.1 200 OK\r\n"
"Date: Sat, 05 Jan 2013 03:13:29 GMT\r\n"
"Vary: Accept-Encoding\r\n"
"Content-Type: text/html; charset=gb2312\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 57\r\n"
"\r\n"
"<html> <head>欢迎光临</head> <body>屌丝逆袭季</body></html>";
fd_set fds;
void echo_srv(int clientFd)
{
//处理用户请求数据
char line[MAX_LINE];
printf( "开始读取数据");
int n = read(clientFd, line, sizeof(line));
if(n < 0)
{
//#define ECONNRESET 104 /* Connection reset by peer */
if(errno == ECONNRESET)
{
close(clientFd);
FD_CLR(clientFd, &fds);
printf("异常退出\n");
}
else
{
printf("网络异常");
exit(-1);
}
}
else if(n == 0)
{
close(clientFd);
FD_CLR(clientFd, &fds);
printf("正常退出\n");
}
else
{
line
= 0;
printf("接收到数据:%s\n", line);
write(clientFd, szHtmlBuf, sizeof(szHtmlBuf));
}
}
int main()
{
struct sockaddr_in cliaddr, servaddr;
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
if( listenFd < 0)
{
printf("socket函数执行失败");
return 1;
}
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_aton('10.132.10.64', &(servaddr.sin_addr));
//servaddr.sin_addr.s_addr = inet_addr("10.132.10.64");
servaddr.sin_port = htons(SERV_PORT);
if(bind(listenFd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("bind函数执行失败");
return 1;
}
if(listen(listenFd, LISTENQ) < 0)
{
printf("listen函数执行失败");
return 1;
}
printf("listen函数执行成功\n");
//select 部分
int maxfd;
FD_ZERO(&fds);
do{
FD_SET(listenFd, &fds);
maxfd = listenFd + 1;
int nRead = 0;
if( (nRead = select(maxfd + 1, &fds, NULL, NULL, NULL)) < 0)
{
printf("select 失败\n");
exit(-1);
}
printf("select find data Change\n");
for(int i = 0; i <= maxfd && nRead > 0; i++)
{
if(!FD_ISSET(i, &fds))
{
continue;
}
--nRead;
if(i == listenFd)
{
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenFd, (struct sockaddr*)&cliaddr, &clilen);
if(connfd < 0)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
{
printf( "fail to accept new client \n");
continue;
}
}
printf("Ip: %s 到此一游\n", inet_ntoa(cliaddr.sin_addr));
FD_SET(connfd, &fds);
maxfd = (connfd > maxfd ? connfd : maxfd);
}
else
{
echo_srv(i);
}
}
}while(true);
return 0;
}
直接用浏览器进行测试即可:http://服务器Ip地址:10088
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2014-01/94777.htm
在网络上看到太多关于select i/o用法,都不是非常的详细。
经过摸索,把我实现的例子张贴上来,供大家参考。
描述:
服务器:实现并发tcp服务器,最多允许64 个客户端连接。
客户端:与服务器建立tcp连接,发送数据给服务器。服务器接收数据。
服务器代码:(红色为我的代码)
// SelectServerDlg.h : header file
//
#if !defined(AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_)
#define AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CSelectServerDlg dialog
UINT proc(LPVOID lParam);
class CSelectServerDlg : public CDialog
{
// Construction
public:
CSelectServerDlg(CWnd* pParent = NULL); // standard constructor
CWinThread *pThread;
BOOL bRun;
// Dialog Data
//{{AFX_DATA(CSelectServerDlg)
enum { IDD = IDD_SELECTSERVER_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSelectServerDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CSelectServerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_SELECTSERVERDLG_H__3D5E9C67_8737_4E23_9734_0F196495ED30__INCLUDED_)
————————————————————————————————————————
//SelectServerDlg.cpp代码
BOOL CSelectServerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
WSAData wsadata;
WORD wVersionRequest;
wVersionRequest=MAKEWORD(2,2);
if(WSAStartup(wVersionRequest,&wsadata)!=0)
TRACE("没有DLL可用\n");
if(LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2)
{
AfxMessageBox("socket dll 启动失败");
WSACleanup();
}
pThread = AfxBeginThread(proc, this);
return TRUE; // return TRUE unless you set the focus to a control
}
——————————————————————————————————————
//服务器线程
UINT proc(LPVOID lParam)
{
try{
fd_set rset;
CSelectServerDlg *pDlg = (CSelectServerDlg*)lParam;
struct sockaddr_in seraddr,cliaddr;
int client[FD_SETSIZE],listensock;
FD_ZERO(&rset);
seraddr.sin_addr.s_addr = inet_addr("192.168.1.24");
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(6500);
memset(seraddr.sin_zero, 0, 8);
listensock = socket(AF_INET, SOCK_STREAM, 0);
TRACE("listen socket :%d\n", listensock);
bind(listensock, (struct sockaddr *)&seraddr, sizeof(seraddr));
listen(listensock, 20);
for(int i = 0; i<FD_SETSIZE; i++)
client[i] = -1;
pDlg->bRun = TRUE;
int nReady;
struct timeval timeout;
int isset;
int err;
int maxfd;
int connfd;
int clilen;
maxfd = listensock;
char buf[1024];
int recvbytes = 0;
memset(buf, 0, 1024);
while(pDlg->bRun){
Sleep(1);
timeout.tv_sec = 0;
timeout.tv_usec = 10;
/*
listen socket 加入fdset
*/
FD_SET(listensock, &rset);
for(int n = 0; n<FD_SETSIZE; n++)
{
if(client
>0){
FD_SET(client
, &rset);
if(client
> maxfd)
maxfd = client
;
}
}
nReady = select(maxfd+1, &rset, NULL, NULL, &timeout);
if(nReady == 0)
//TRACE("select fun time out\n");
;
else if(nReady == -1)
{
TRACE("socket error\n");
err = WSAGetLastError();
switch(err)
{
case WSANOTINITIALISED:
TRACE("WSANOTINITIALISED 未初始化\n");
break;
case WSAEFAULT:
TRACE("WSAEFAULT\n");
break;
case WSAENETDOWN:
TRACE("WSAENETDOWN\n");
break;
case WSAEINVAL:
TRACE("WSAEINVAL\n");
break;
case WSAEINTR:
TRACE("WSAEINTR\n");
break;
case WSAEINPROGRESS:
TRACE("WSAEINPROGRESS\n");
break;
case WSAENOTSOCK:
TRACE("WSAENOTSOCK\n");
break;
}
}
else
{
/*
测试已经建立连接的socket
*/
unsigned long ul = 1;
for(int j = 0; j<FD_SETSIZE; j++)
{
if(client[j]>0)
{
memset(buf,0,1024);
isset = FD_ISSET(client[j],&rset);
if(isset)
{
//ioctlsocket(client[j], FIONBIO, &ul);
recvbytes = recv(client[j], buf, 1024, NULL);
if(recvbytes>0)
TRACE("from socket: %d,接收字节数:%d,%s\n",client[j], recvbytes, buf);
else if(recvbytes == 0)
{
closesocket(client[j]);
TRACE("client socket :%d closed! \n",client[j]);
client[j] = -1;
}
}
}
}
/*
测试是否有连接?
*/
isset = FD_ISSET(listensock, &rset);
FD_ZERO(&rset);
if(isset)
{
TRACE("conncetion.... \n");
clilen = sizeof(cliaddr);
connfd = accept(listensock,(struct sockaddr *)&cliaddr, &clilen);
TRACE("connection comes from ip:%d.%d.%d.%d\n",cliaddr.sin_addr.S_un.S_un_b.s_b1,
cliaddr.sin_addr.S_un.S_un_b.s_b2,
cliaddr.sin_addr.S_un.S_un_b.s_b3,
cliaddr.sin_addr.S_un.S_un_b.s_b4);
for(int s=0; s<FD_SETSIZE; s++){
if(client[s]<0){
client[s] = connfd;
break;
}
}
TRACE("con number:%d\n", s+1);
TRACE("socket id:");
for(int k = 0; k < FD_SETSIZE; k++)
{
if(client[k]>0)
TRACE("%d,",client[k]);
}
TRACE("\n");
}
}
}
}
catch (...) {
TRACE("exception.........!!\n");
}
return TRUE;
}
——————————————————————————————————
//客户端代码:
BOOL CTcpClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
WSAData wsadata;
WORD wVersionRequest;
wVersionRequest=MAKEWORD(2,2);
if(WSAStartup(wVersionRequest,&wsadata)!=0)
TRACE("没有DLL可用\n");
if(LOBYTE(wsadata.wVersion)!=2||HIBYTE(wsadata.wVersion)!=2)
{
AfxMessageBox("socket dll 启动失败");
WSACleanup();
}
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(6500);
serveraddr.sin_addr.S_un.S_un_b.s_b1=192;
serveraddr.sin_addr.S_un.S_un_b.s_b2=168;
serveraddr.sin_addr.S_un.S_un_b.s_b3=1;
serveraddr.sin_addr.S_un.S_un_b.s_b4=24;
clientsocket=socket(AF_INET,SOCK_STREAM,0);
return TRUE; // return TRUE unless you set the focus to a control
}
void CTcpClientDlg::OnSend()
{
// TODO: Add your control notification handler code here
nResult=connect(clientsocket,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("连接成功\n");
char *szSend="123456789";
nResult=sendto(clientsocket,szSend,9,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("发送成功\n");
}
}
}
void CTcpClientDlg::OnButton2()
{
// TODO: Add your control notification handler code here
closesocket(clientsocket);
}
void CTcpClientDlg::OnButton3()
{
// TODO: Add your control notification handler code here
char *szSend="123456789";
nResult=sendto(clientsocket,szSend,9,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(nResult != SOCKET_ERROR)
{
TRACE("发送成功\n");
}
}
(完)
在很长的时间内,相比于Windows平台众多的选择,select模式都是linux平台下网络通信的不多选择之一(在epoll未出生之前)。这种模式在面对大量短连接的时候,比OCOT有高的效率,可以避免频繁的上下文切换。
select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到遇到以下情况之一select会退出:
(1) 监控的句柄至少有一个发生变化,返回值大于1。
(2) 等待时间已到,返回值等于0。
(3) 收到信号退出,返回值小于0。
select操作的内部会在指定的时间内,不断的检测受控集合中的套接字是否可读,可写或有错误,在select返回时只保留发生状态变化的套接字。通过判断select的返回值,就可以了解该调用是否有文件描述符可读或是select调用退出等信息。
要想让程序运行,光有一个select的调用不是不够的,我们还需要一系列的配套数据结构和算法来帮助将套接字归类,判断是否有指定的套接字等等,结构体fd_set和宏FD_ZERO,FD_CLR,FD_SET,FD_ISSET等就是我们所需要的。通过定义一个fd_set的对象,例如可读集合,有相同需求的套接字都可以放置到这个对象中,可以通过FD_SET这个宏来完成,当然在这之间需要利用FD_ZERO将这个对象初始化。等完成了以上操作后,便会调用select查询是否有套接字可读。等待select完成后,如果有套接字可读,就可以利用FD_ISSET判断是否为监听套接字,转而执行accept操作。如果一个套接字已经关闭,就可以利用FD_CLR将其从fd_set对象中剔除。
服务器端源代码:
1
#include <iostream>
2
#include <sys/socket.h>
3
#include <sys/types.h>
4
#include <arpa/inet.h>
5
#include <sys/select.h>
6
#include <sys/ioctl.h>
7
using namespace std;
8
9
int main()
10
{
11
12
int server_sock = socket(AF_INET,SOCK_STREAM,0);
13
if(server_sock < 0)
14
{
15
cout << "create socket error" << endl;
16
return -1;
17
}
18
19
struct sockaddr_in server_addr;
20
memset(&server_addr,0,sizeof(server_addr));
21
server_addr.sin_family = AF_INET;
22
server_addr.sin_port = htons(5555);
23
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
24
25
if(bind(server_sock,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
26
{
27
cout << "bind socket error" << endl;
28
close(server_sock);
29
return -1;
30
}
31
32
listen(server_sock,10);
33
34
fd_set read_set;
35
fd_set test_set;
36
FD_ZERO(&read_set);
37
FD_SET(server_sock,&read_set);
38
39
struct timeval tm;
40
tm.tv_sec = 5;
41
tm.tv_usec = 500;
42
43
44
while(true)
45
{
46
int nread = 0;
47
test_set = read_set;
48
int ret = select(FD_SETSIZE,&test_set,NULL,NULL,&tm);
49
50
if(ret < 0)
51
{
52
cout << "select error" << endl;
53
close(server_sock);
54
return -1;
55
}
56
else if(ret == 0)
57
{
58
//cout << "waitout" << endl;
59
continue;
60
}
61
else
62
{
63
for(int fd=0; fd<FD_SETSIZE; ++fd)
64
{
65
if(FD_ISSET(fd,&test_set))
66
{
67
//如果有新的连接到达
68
if(fd == server_sock)
69
{
70
int sock = accept(server_sock,NULL,NULL);
71
FD_SET(sock,&read_set);
72
}
73
else
74
{
75
ioctl(fd,FIONREAD,&nread);
76
77
if(nread == 0) //客户端已经关闭
78
{
79
close(fd);
80
FD_CLR(fd,&read_set);
81
cout << "client has removed " << fd << endl;
82
}
83
else
84
{
85
char buf[128];
86
recv(fd,buf,128,0);
87
cout << buf << endl;
88
send(fd,buf,strlen(buf)+1,0);
89
}
90
}
91
92
}
93
94
}
95
96
}
97
98
}
99
}
100
相关文章推荐
- ProtoBuf
- https 对接非对称密钥加密 MD5及数字签名相关
- TCP and UDP Small Servers
- 【iOS】MD5加密与网络数据安全
- 网络请求之GET、POST请求
- http错误码
- https://123.56.95.148/svn/Source/secretary
- 7月第3周网络安全报告:应用程序漏洞居首 涨27.7%
- JAVA网络编程-----TCP沟通
- Java网络编程基础
- 大黄蜂(HORNET):比Tor更快更安全的匿名网络
- Source Insight基本使用和快捷键 http://www.cnblogs.com/mengdd/p/3506526.html
- SCVMM 2012 R2运维管理十四之:VMM中网络概述
- SCVMM 2012 R2运维管理十四之:VMM中网络概述
- poj 3469 dinic网络流模板
- 摘:MD231 gprs模块建立tcp或udp连接,AT命令详细参考
- tcp超时重传
- 看你的门-攻击服务器(4)-HTTP参数注入攻击
- iOS开发网络篇—大文件的多线程断点下载
- 校园网络维护脚本