您的位置:首页 > 理论基础 > 计算机网络

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: