linux下网络服务器模型以及使用时应该注意的问题
来源:互联网 发布:免费淘宝卖家管理软件 编辑:程序博客网 时间:2024/05/21 21:39
UDP编程应该注意的问题:
服务器端程序编写流程:
最常用的服务器模型.
创建一个socket句柄,然后绑定到本地端口上,然后创建一个线程进行接受和发送数据!(注意:一般是先接受,再发送,因为udp是面向无连接的服务,因此需要先通过recvfrom得到客户端的IP和端口信息(addr_remote),才能进行数据的收发)一般需要两个结构体struct sockaddr_in,一个是本地IP和端口信息,另一个是用于接受客户端的端口和IP;
第二点需要注意就是recvfrom和sendto的第六个参数类型是不一样的,recvfrom是socklen_t*而sendto是socklen_t,就这点错误让我折腾了差不多半天!唉!粗心大意真是害人呀!!!!!!!!!!!!!!!!!!!!!
3.1. 循环服务器:
循环服务器在同一个时刻只可以响应一个客户端的请求
3.1.1. 循环服务器之UDP服务器
UDP循环服务器的实现非常简单:UDP服务器每次从套接字上读取一个客户端的请求,处理, 然后将结果返回给客户机.
可以用下面的算法来实现.
socket(...);
bind(...);
while(1)
{
recvfrom(...);
process(...);
sendto(...);
}
**
**
因为UDP是非面向连接的,没有一个客户端可以老是占住服务端. 只要处理过程不是死循环, 服务器对于每一个客户机的请求总是能够满足.
**
3.1.2. 循环服务器之TCP服务器
TCP循环服务器的实现也不难:TCP服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
}
**
**
TCP循环服务器一次只能处理一个客户端的请求.只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求.这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了.因此,TCP服务器一般很少用循环服务器模型的.
**
3.2. 并发服务器
并发服务器在同一个时刻可以响应多个客户端的请求
**
3.2.1. 并发服务器之TCP服务器
为了弥补循环TCP服务器的缺陷,人们又想出了并发服务器的模型. 并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个 子进程来处理.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
if(fork(..)==0)
{
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
exit(...);
}
close(...);
}
**
**
TCP并发服务器可以解决TCP循环服务器客户机独占服务器的情况. 不过也同时带来了一个不小的问题.为了响应客户机的请求,服务器要创建子进程来处理. 而创建子进程是一种非常消耗资源的操作.
**
3.2.2. 并发服务器之多路复用I/O
为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型.
首先介绍一个函数select
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *except fds,struct timeval *timeout)
void FD_SET(int fd,fd_set *fdset)
void FD_CLR(int fd,fd_set *fdset)
void FD_ZERO(fd_set *fdset)
int FD_ISSET(int fd,fd_set *fdset)
**
一般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读(通信的对方还没有 发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不希望阻塞,我们的一个选择是用select系统调用. 只要我们设置好select的各个参数,那么当文件可以读写的时候select回”通知”我们说可以读写了.
readfds所有要读的文件文件描述符的集合
writefds所有要的写文件文件描述符的集合
exceptfds其他的服要向我们通知的文件描述符
timeout超时设置.
nfds所有我们监控的文件描述符中最大的那一个加1
在我们调用select时进程会一直阻塞直到以下的一种情况发生.
1)有文件可以读.
2)有文件可以写.
3)超时所设置的时间到.
**
为了设置文件描述符我们要使用几个宏.
FD_SET将fd加入到fdset
FD_CLR将fd从fdset里面清除
FD_ZERO从fdset中清除所有的文件描述符
FD_ISSET判断fd是否在fdset集合中
使用select的一个例子
int use_select(int *readfd,int n)
{
fd_set my_readfd;
int maxfd;
int i;
**
maxfd=readfd[0];
for(i=1;i<n;i++)
if(readfd>maxfd)
maxfd=readfd;
while(1)
{
/* 将所有的文件描述符加入 */
FD_ZERO(&my_readfd);
for(i=0;i FD_SET(readfd,*my_readfd);
/* 进程阻塞 */
select(maxfd+1,& my_readfd,NULL,NULL,NULL);
/* 有东西可以读了 */
for(i=0;i if(FD_ISSET(readfd,&my_readfd))
{
/* 原来是我可以读了 */
we_read(readfd);
}
}
}
**
**
使用select后我们的服务器程序就变成了.
初始化(socket,bind,listen);
while(1)
{
设置监听读写文件描述符(FD_*);
调用select;
如果是倾听套接字就绪,说明一个新的连接请求建立
{
建立连接(accept);
加入到监听文件描述符中去;
}
否则说明是一个已经连接过的描述符
{
进行操作(read或者write);
}
**
}
多路复用I/O可以解决资源限制的问题.着模型实际上是将UDP循环模型用在了TCP上面. 这也就带来了一些问题.如由于服务器依次处理客户的请求,所以可能会导致有的客户 会等待很久.
**
3.2.3. 并发服务器之UDP服务器
人们把并发的概念用于UDP就得到了并发UDP服务器模型. 并发UDP服务器模型其实是简单的.和并发的TCP服务器模型一样是创建一个子进程来处理的 算法和并发的TCP模型一样.
除非服务器在处理客户端的请求所用的时间比较长以外,人们实际上很少用这种模型.
一个并发TCP服务器实例
#include “...”
#define MY_PORT 8888
int main(int argc ,char **argv)
{
int listen_fd,accept_fd;
struct sockaddr_in client_addr;
int n;
**
if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf(“Socket Error:%s/n/a”,strerror(errno));
exit(1);
}
**
bzero(&client_addr,sizeof(struct sockaddr_in));
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(MY_PORT);
client_addr.sin_addr.s_addr=htonl(INADDR_ANY);
n=1;
/* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间 */
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));
if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0)
{
printf(“Bind Error:%s/n/a”,strerror(errno));
exit(1);
}
listen(listen_fd,5);
while(1)
{
accept_fd=accept(listen_fd,NULL,NULL);
if((accept_fd<0)&&(errno==EINTR))
continue;
else if(accept_fd<0)
{
printf(“Accept Error:%s/n/a”,strerror(errno));
continue;
}
if((n=fork())==0)
{
/* 子进程处理客户端的连接 */
char buffer[1024];
close(listen_fd);
n=read(accept_fd,buffer,1024);
write(accept_fd,buffer,n);
close(accept_fd);
exit(0);
}
else if(n<0)
printf(“Fork Error:%s/n/a”,strerror(errno));
close(accept_fd);
}
}
**
**
4. 数据流程
Server
Client
1. Establish a listening socket and wait for connections from clients.
**
**
2. Create a client socket and attempt to connect to server.
3. Accept the client's connection attempt.
**
4. Send and receive data.
4. Send and receive data.
5. Close the connection.
5. Close the connection.
**
**
5. 实例分析
总的来说,利用socket进行网络编程并不难,却有点繁琐,稍不留心,就会出错,在C++网络编程卷一中就举过这样一个例子
Error example of socket
#include <sys/types.h>
#include <sys/socket.h>
**
const int PORT_NUM=2007;
const int BUFSIZE=256;
**
int echo_server()
{
struct sockaddr_in addr;
int addr_len; //error 1 :未初始化addr_len
char buf[BUFSIZE];
int n_handle;
//error 2: s_handle在windows平台上的类型为SOCKET,移植性不好
int s_handle=socket(PF_UNIX,SOCK_DGRAM,0);
if(s_handle==-1) return -1;
// error 3: 整个addr 结构要先清零
// error 4: PF_UNIX应对应 PF_INET
addr.sin_family=AF_INET;
// error 5: PORT_NUM应使用网络字节顺序
addr.sin_port=PORT_NUM;
addr.sin_addr.addr=INSDDR_ANY;
**
if(bind(s_handle,(struct sockaddr*) &addr,sizeof addr)==-1)
return -1;
// error 6: 未调用listen
// error 7: 未加括号,导致运算符优先级问题
// error 8: accept调用错误, 上面的socket调用应用SOCK_STREAM
if(n_handle=accept(s_handle,(struct sockaddr*)&addr, &addr_len)!=-1)
{
int n;
// error 9: read应该读取n_handle,而不是s_handle
while((n=read(s_handle,buf,sizeof(buf))>0)
write(n_handle,buf,n);
// error 9: 没有检查write返回值,有可能造成数据丢失
close(n_handle);
}
return 0;
}
**
所有凡是使用socket编程的程序中都想用一些相对简单的类来封装这些繁琐的接口调用
我也曾经做过这样的尝试
**
/*
* Copyright 2005 JinWei Bird Studio All rights reserved
*
* Filename: wf_socket.h
* Description: Test program of socket lib
*
* Version:1.0
* Create date: 08/19/2005
* Author: Walter Fan, walter.fan@gmail.com
*/
#include "wf_base.h"
**
#ifndef BACKLOG
#define BACKLOG 50
#endif
**
#ifndef HOSTLEN
#define HOSTLEN 256
#endif
**
class Socket
{
protected:
int m_nPort;
int m_nSock;
int m_nBacklog;
char* m_szHost;
**
bool m_bServer;
fd_set m_fdSet;
int m_fdNum;
public:
Socket(int port);
Socket(char* host,int port);
virtual ~Socket();
virtual int Wait()=0;//encapsulate select and accept
virtual int Open()=0;//encapsulate socket,listen or connect
int Close();//encapsulate close socket handle
int GetSocketID();
int CloseFD(int fd);//encapsulate close file handle
};
**
Socket::Socket(char* host,int port)
:m_szHost(host),m_nPort(port),m_bServer(false),m_fdNum(0)
{
m_nSock=-1;
m_nBacklog=BACKLOG;
FD_ZERO(&m_fdSet);
msg_trace("Socket construct as Client...");
}
**
Socket::Socket(int port)
:m_szHost("127.0.0.1"),m_nPort(port),m_bServer(true),m_fdNum(0)
{
m_nSock=-1;
m_nBacklog=BACKLOG;
FD_ZERO(&m_fdSet);
msg_trace("Socket construct as Server...");
}
**
Socket::~Socket()
{
Close();
msg_trace("Socket destruct...");
}
**
**
int Socket::Close()//encapsulate close socket handle
{
if (m_bServer)
{
for (int fd = 0; fd <= m_fdNum; fd++)
{
if (FD_ISSET(fd, &m_fdSet))
close(fd);
}
}
else
{
close(m_nSock);
}
return 0;
**
}
int Socket::GetSocketID()
{
return m_nSock;
}
**
int Socket::CloseFD(int fd)//encapsulate close file handle
{
int retval=0;
retval=close(fd);
if(retval<0)
return retval;
FD_CLR(fd, &m_fdSet);
m_fdNum--;
return retval;
}
**
//------------------------TCP --------------------//
class TCPSocket:public Socket
{
**
public:
TCPSocket(int port):Socket(port){};
TCPSocket(char* host,int port):Socket(host,port){};
int Wait();
int Open();
};
**
**
int TCPSocket::Open()
{
int retval=0;
//int sock_id; // the socket
struct sockaddr_in saddr; // build our address here
struct hostent *hp; // this is part of our
m_nSock = socket(AF_INET, SOCK_STREAM, 0); // get a socket
if ( m_nSock == -1 )
return -1;
if (m_nSock > m_fdNum)
m_fdNum = m_nSock;
//---set socket option---//
int socket_option_value = 1;
retval=setsockopt(m_nSock, SOL_SOCKET, SO_REUSEADDR,
&socket_option_value, sizeof(socket_option_value));
if(retval<0)
return -1;
//---build address and bind it to socket---//
**
bzero((char *)&saddr, sizeof(saddr)); // clear out struct
gethostname(m_szHost, HOSTLEN); // where am I ?
hp = gethostbyname(m_szHost); // get info about host
if (hp == NULL)
return -1; // fill in host part
bcopy((char *)hp->h_addr, (char *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(m_nPort); // fill in socket port
saddr.sin_family = AF_INET ; // fill in addr family
if(m_bServer)
{
retval=bind(m_nSock, (struct sockaddr *)&saddr, sizeof(saddr));
if (retval!= 0 )
return -1;
//---arrange for incoming calls---//
retval=listen(m_nSock, m_nBacklog);
if ( retval!= 0 )
return -1;
FD_SET(m_nSock,&m_fdSet);
}
else
{
retval=connect(m_nSock,(struct sockaddr *)&saddr, sizeof(saddr));
//msg_trace("connect return "<<retval);
if (retval!=0)
return -1;
}
return m_nSock;
}
**
int TCPSocket::Wait()
{
int retval=0;
if(m_bServer)
{
fd_set fd_set_read;
int fd,clientfd;
struct sockaddr_un from;
socklen_t from_len=sizeof(from);
while(true)
{
//msg_trace("select begin...");
retval=select(m_fdNum+1,&m_fdSet,NULL,NULL,NULL);
//msg_trace("select return "<<retval);
if(retval<0)
return -1;
for(fd=0;fd<=m_fdNum;fd++)
{
if(FD_ISSET(fd,&m_fdSet))
{
if(fd==m_nSock)
{
clientfd=accept(m_nSock,(struct sockaddr*)&from,&from_len);
//msg_trace("accept return "<<clientfd);
if(clientfd<0)
return -1;
FD_SET(clientfd,&m_fdSet);
m_fdNum++;
continue;
}
else
return fd;
}
}
}
}
return retval;
}
**
int main(int argc, char *argv[])
{
FILE* fp;
time_t thetime;
if(fork()==0)//client side
{
int sock, ret=0;
char buf[100];
TCPSocket oSock("127.0.0.1",1975);
while((sock =oSock.Open())==-1);
ret=write(sock,"hi,walter",10);
if(ret<0) err_quit("write error");
ret=read(sock,buf,sizeof(buf));
if(ret<0) err_quit("read error");
msg_trace("Client get "<<buf);
}
else//server side
{
int fd, ret=0;
char buf[100];
TCPSocket oSock(1975);
oSock.Open();
fd = oSock.Wait();
if(fd<0) err_quit("wait failed");
ret=read(fd,buf,sizeof(buf));
if(ret<0) err_quit("read failed");
msg_trace("Server get "<<buf);
ret=write(fd,"Good bye",10);
if(ret<0) err_quit("wait failed");
oSock.CloseFD(fd);
}
return 0;
}
**
**
服务器端程序编写流程:
最常用的服务器模型.
创建一个socket句柄,然后绑定到本地端口上,然后创建一个线程进行接受和发送数据!(注意:一般是先接受,再发送,因为udp是面向无连接的服务,因此需要先通过recvfrom得到客户端的IP和端口信息(addr_remote),才能进行数据的收发)一般需要两个结构体struct sockaddr_in,一个是本地IP和端口信息,另一个是用于接受客户端的端口和IP;
第二点需要注意就是recvfrom和sendto的第六个参数类型是不一样的,recvfrom是socklen_t*而sendto是socklen_t,就这点错误让我折腾了差不多半天!唉!粗心大意真是害人呀!!!!!!!!!!!!!!!!!!!!!
3.1. 循环服务器:
循环服务器在同一个时刻只可以响应一个客户端的请求
3.1.1. 循环服务器之UDP服务器
UDP循环服务器的实现非常简单:UDP服务器每次从套接字上读取一个客户端的请求,处理, 然后将结果返回给客户机.
可以用下面的算法来实现.
socket(...);
bind(...);
while(1)
{
recvfrom(...);
process(...);
sendto(...);
}
**
**
因为UDP是非面向连接的,没有一个客户端可以老是占住服务端. 只要处理过程不是死循环, 服务器对于每一个客户机的请求总是能够满足.
**
3.1.2. 循环服务器之TCP服务器
TCP循环服务器的实现也不难:TCP服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
}
**
**
TCP循环服务器一次只能处理一个客户端的请求.只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求.这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了.因此,TCP服务器一般很少用循环服务器模型的.
**
3.2. 并发服务器
并发服务器在同一个时刻可以响应多个客户端的请求
**
3.2.1. 并发服务器之TCP服务器
为了弥补循环TCP服务器的缺陷,人们又想出了并发服务器的模型. 并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个 子进程来处理.
算法如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
if(fork(..)==0)
{
while(1)
{
read(...);
process(...);
write(...);
}
close(...);
exit(...);
}
close(...);
}
**
**
TCP并发服务器可以解决TCP循环服务器客户机独占服务器的情况. 不过也同时带来了一个不小的问题.为了响应客户机的请求,服务器要创建子进程来处理. 而创建子进程是一种非常消耗资源的操作.
**
3.2.2. 并发服务器之多路复用I/O
为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型.
首先介绍一个函数select
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *except fds,struct timeval *timeout)
void FD_SET(int fd,fd_set *fdset)
void FD_CLR(int fd,fd_set *fdset)
void FD_ZERO(fd_set *fdset)
int FD_ISSET(int fd,fd_set *fdset)
**
一般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读(通信的对方还没有 发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不希望阻塞,我们的一个选择是用select系统调用. 只要我们设置好select的各个参数,那么当文件可以读写的时候select回”通知”我们说可以读写了.
readfds所有要读的文件文件描述符的集合
writefds所有要的写文件文件描述符的集合
exceptfds其他的服要向我们通知的文件描述符
timeout超时设置.
nfds所有我们监控的文件描述符中最大的那一个加1
在我们调用select时进程会一直阻塞直到以下的一种情况发生.
1)有文件可以读.
2)有文件可以写.
3)超时所设置的时间到.
**
为了设置文件描述符我们要使用几个宏.
FD_SET将fd加入到fdset
FD_CLR将fd从fdset里面清除
FD_ZERO从fdset中清除所有的文件描述符
FD_ISSET判断fd是否在fdset集合中
使用select的一个例子
int use_select(int *readfd,int n)
{
fd_set my_readfd;
int maxfd;
int i;
**
maxfd=readfd[0];
for(i=1;i<n;i++)
if(readfd>maxfd)
maxfd=readfd;
while(1)
{
/* 将所有的文件描述符加入 */
FD_ZERO(&my_readfd);
for(i=0;i FD_SET(readfd,*my_readfd);
/* 进程阻塞 */
select(maxfd+1,& my_readfd,NULL,NULL,NULL);
/* 有东西可以读了 */
for(i=0;i if(FD_ISSET(readfd,&my_readfd))
{
/* 原来是我可以读了 */
we_read(readfd);
}
}
}
**
**
使用select后我们的服务器程序就变成了.
初始化(socket,bind,listen);
while(1)
{
设置监听读写文件描述符(FD_*);
调用select;
如果是倾听套接字就绪,说明一个新的连接请求建立
{
建立连接(accept);
加入到监听文件描述符中去;
}
否则说明是一个已经连接过的描述符
{
进行操作(read或者write);
}
**
}
多路复用I/O可以解决资源限制的问题.着模型实际上是将UDP循环模型用在了TCP上面. 这也就带来了一些问题.如由于服务器依次处理客户的请求,所以可能会导致有的客户 会等待很久.
**
3.2.3. 并发服务器之UDP服务器
人们把并发的概念用于UDP就得到了并发UDP服务器模型. 并发UDP服务器模型其实是简单的.和并发的TCP服务器模型一样是创建一个子进程来处理的 算法和并发的TCP模型一样.
除非服务器在处理客户端的请求所用的时间比较长以外,人们实际上很少用这种模型.
一个并发TCP服务器实例
#include “...”
#define MY_PORT 8888
int main(int argc ,char **argv)
{
int listen_fd,accept_fd;
struct sockaddr_in client_addr;
int n;
**
if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf(“Socket Error:%s/n/a”,strerror(errno));
exit(1);
}
**
bzero(&client_addr,sizeof(struct sockaddr_in));
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(MY_PORT);
client_addr.sin_addr.s_addr=htonl(INADDR_ANY);
n=1;
/* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间 */
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));
if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0)
{
printf(“Bind Error:%s/n/a”,strerror(errno));
exit(1);
}
listen(listen_fd,5);
while(1)
{
accept_fd=accept(listen_fd,NULL,NULL);
if((accept_fd<0)&&(errno==EINTR))
continue;
else if(accept_fd<0)
{
printf(“Accept Error:%s/n/a”,strerror(errno));
continue;
}
if((n=fork())==0)
{
/* 子进程处理客户端的连接 */
char buffer[1024];
close(listen_fd);
n=read(accept_fd,buffer,1024);
write(accept_fd,buffer,n);
close(accept_fd);
exit(0);
}
else if(n<0)
printf(“Fork Error:%s/n/a”,strerror(errno));
close(accept_fd);
}
}
**
**
4. 数据流程
Server
Client
1. Establish a listening socket and wait for connections from clients.
**
**
2. Create a client socket and attempt to connect to server.
3. Accept the client's connection attempt.
**
4. Send and receive data.
4. Send and receive data.
5. Close the connection.
5. Close the connection.
**
**
5. 实例分析
总的来说,利用socket进行网络编程并不难,却有点繁琐,稍不留心,就会出错,在C++网络编程卷一中就举过这样一个例子
Error example of socket
#include <sys/types.h>
#include <sys/socket.h>
**
const int PORT_NUM=2007;
const int BUFSIZE=256;
**
int echo_server()
{
struct sockaddr_in addr;
int addr_len; //error 1 :未初始化addr_len
char buf[BUFSIZE];
int n_handle;
//error 2: s_handle在windows平台上的类型为SOCKET,移植性不好
int s_handle=socket(PF_UNIX,SOCK_DGRAM,0);
if(s_handle==-1) return -1;
// error 3: 整个addr 结构要先清零
// error 4: PF_UNIX应对应 PF_INET
addr.sin_family=AF_INET;
// error 5: PORT_NUM应使用网络字节顺序
addr.sin_port=PORT_NUM;
addr.sin_addr.addr=INSDDR_ANY;
**
if(bind(s_handle,(struct sockaddr*) &addr,sizeof addr)==-1)
return -1;
// error 6: 未调用listen
// error 7: 未加括号,导致运算符优先级问题
// error 8: accept调用错误, 上面的socket调用应用SOCK_STREAM
if(n_handle=accept(s_handle,(struct sockaddr*)&addr, &addr_len)!=-1)
{
int n;
// error 9: read应该读取n_handle,而不是s_handle
while((n=read(s_handle,buf,sizeof(buf))>0)
write(n_handle,buf,n);
// error 9: 没有检查write返回值,有可能造成数据丢失
close(n_handle);
}
return 0;
}
**
所有凡是使用socket编程的程序中都想用一些相对简单的类来封装这些繁琐的接口调用
我也曾经做过这样的尝试
**
/*
* Copyright 2005 JinWei Bird Studio All rights reserved
*
* Filename: wf_socket.h
* Description: Test program of socket lib
*
* Version:1.0
* Create date: 08/19/2005
* Author: Walter Fan, walter.fan@gmail.com
*/
#include "wf_base.h"
**
#ifndef BACKLOG
#define BACKLOG 50
#endif
**
#ifndef HOSTLEN
#define HOSTLEN 256
#endif
**
class Socket
{
protected:
int m_nPort;
int m_nSock;
int m_nBacklog;
char* m_szHost;
**
bool m_bServer;
fd_set m_fdSet;
int m_fdNum;
public:
Socket(int port);
Socket(char* host,int port);
virtual ~Socket();
virtual int Wait()=0;//encapsulate select and accept
virtual int Open()=0;//encapsulate socket,listen or connect
int Close();//encapsulate close socket handle
int GetSocketID();
int CloseFD(int fd);//encapsulate close file handle
};
**
Socket::Socket(char* host,int port)
:m_szHost(host),m_nPort(port),m_bServer(false),m_fdNum(0)
{
m_nSock=-1;
m_nBacklog=BACKLOG;
FD_ZERO(&m_fdSet);
msg_trace("Socket construct as Client...");
}
**
Socket::Socket(int port)
:m_szHost("127.0.0.1"),m_nPort(port),m_bServer(true),m_fdNum(0)
{
m_nSock=-1;
m_nBacklog=BACKLOG;
FD_ZERO(&m_fdSet);
msg_trace("Socket construct as Server...");
}
**
Socket::~Socket()
{
Close();
msg_trace("Socket destruct...");
}
**
**
int Socket::Close()//encapsulate close socket handle
{
if (m_bServer)
{
for (int fd = 0; fd <= m_fdNum; fd++)
{
if (FD_ISSET(fd, &m_fdSet))
close(fd);
}
}
else
{
close(m_nSock);
}
return 0;
**
}
int Socket::GetSocketID()
{
return m_nSock;
}
**
int Socket::CloseFD(int fd)//encapsulate close file handle
{
int retval=0;
retval=close(fd);
if(retval<0)
return retval;
FD_CLR(fd, &m_fdSet);
m_fdNum--;
return retval;
}
**
//------------------------TCP --------------------//
class TCPSocket:public Socket
{
**
public:
TCPSocket(int port):Socket(port){};
TCPSocket(char* host,int port):Socket(host,port){};
int Wait();
int Open();
};
**
**
int TCPSocket::Open()
{
int retval=0;
//int sock_id; // the socket
struct sockaddr_in saddr; // build our address here
struct hostent *hp; // this is part of our
m_nSock = socket(AF_INET, SOCK_STREAM, 0); // get a socket
if ( m_nSock == -1 )
return -1;
if (m_nSock > m_fdNum)
m_fdNum = m_nSock;
//---set socket option---//
int socket_option_value = 1;
retval=setsockopt(m_nSock, SOL_SOCKET, SO_REUSEADDR,
&socket_option_value, sizeof(socket_option_value));
if(retval<0)
return -1;
//---build address and bind it to socket---//
**
bzero((char *)&saddr, sizeof(saddr)); // clear out struct
gethostname(m_szHost, HOSTLEN); // where am I ?
hp = gethostbyname(m_szHost); // get info about host
if (hp == NULL)
return -1; // fill in host part
bcopy((char *)hp->h_addr, (char *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(m_nPort); // fill in socket port
saddr.sin_family = AF_INET ; // fill in addr family
if(m_bServer)
{
retval=bind(m_nSock, (struct sockaddr *)&saddr, sizeof(saddr));
if (retval!= 0 )
return -1;
//---arrange for incoming calls---//
retval=listen(m_nSock, m_nBacklog);
if ( retval!= 0 )
return -1;
FD_SET(m_nSock,&m_fdSet);
}
else
{
retval=connect(m_nSock,(struct sockaddr *)&saddr, sizeof(saddr));
//msg_trace("connect return "<<retval);
if (retval!=0)
return -1;
}
return m_nSock;
}
**
int TCPSocket::Wait()
{
int retval=0;
if(m_bServer)
{
fd_set fd_set_read;
int fd,clientfd;
struct sockaddr_un from;
socklen_t from_len=sizeof(from);
while(true)
{
//msg_trace("select begin...");
retval=select(m_fdNum+1,&m_fdSet,NULL,NULL,NULL);
//msg_trace("select return "<<retval);
if(retval<0)
return -1;
for(fd=0;fd<=m_fdNum;fd++)
{
if(FD_ISSET(fd,&m_fdSet))
{
if(fd==m_nSock)
{
clientfd=accept(m_nSock,(struct sockaddr*)&from,&from_len);
//msg_trace("accept return "<<clientfd);
if(clientfd<0)
return -1;
FD_SET(clientfd,&m_fdSet);
m_fdNum++;
continue;
}
else
return fd;
}
}
}
}
return retval;
}
**
int main(int argc, char *argv[])
{
FILE* fp;
time_t thetime;
if(fork()==0)//client side
{
int sock, ret=0;
char buf[100];
TCPSocket oSock("127.0.0.1",1975);
while((sock =oSock.Open())==-1);
ret=write(sock,"hi,walter",10);
if(ret<0) err_quit("write error");
ret=read(sock,buf,sizeof(buf));
if(ret<0) err_quit("read error");
msg_trace("Client get "<<buf);
}
else//server side
{
int fd, ret=0;
char buf[100];
TCPSocket oSock(1975);
oSock.Open();
fd = oSock.Wait();
if(fd<0) err_quit("wait failed");
ret=read(fd,buf,sizeof(buf));
if(ret<0) err_quit("read failed");
msg_trace("Server get "<<buf);
ret=write(fd,"Good bye",10);
if(ret<0) err_quit("wait failed");
oSock.CloseFD(fd);
}
return 0;
}
**
**
- linux下网络服务器模型以及使用时应该注意的问题
- CKeditor的使用以及一些应该注意的问题
- linux下的一个网络服务器模型
- ssh 使用时应该注意的问题
- memcache实践以及应该注意的问题
- 对服务器数据库进行更新时应该注意的问题
- 网站服务器应该注意的问题
- cvs服务器在redhat 9.0上的建立以及应该注意的问题
- ubunto下java以及android环境变量的配置等其他新手应该注意的问题
- 12个最应该使用的Linux服务器OS(下)
- 12个最应该使用的Linux服务器OS(下)
- LINUX应该注意的S位问题
- Linux关机时应该注意的问题
- linux 多线程应该注意的问题
- Linux下web服务器搭建需注意的问题
- 对于使用xml配置文档时应该注意的问题
- 在开发WML时使用标签应该注意的问题
- vector 容器使用时应该注意的内存分配问题
- java堆栈 (转)
- 移植 ffmpeg 到 ARM 平台
- 数据库的设计三范式
- 南京师范大学自考-一些常识
- Eclipse快捷键大全(转载)
- linux下网络服务器模型以及使用时应该注意的问题
- SQL Server : Browser服务是干什么的
- having使用
- SQL中cross join,left join,right join ,full join,inner join 的区别
- JRTPLIB编译说出现的问题及解决方法(转载)
- hashmap的使用
- String对象的相关知识(转载)
- SQL Server 2005 SP3 中的新增功能
- 半年以后