应用层 | 应用程序:FTP(文件传输)、E-mail、Telnet(网络) |
表示层 | 数据格式定义、数据转换/加密 |
会话层 | |
传输层 | |
网络层 | |
数据链路层 | 数据组成可发送、接收的帧 |
物理层 |
应用层 | Telnet、FTP、HTTP(超文本传输协议)、DNS(域名解析)、SMTP(邮件传输)、ARP(地址解析协议) |
传输层 | TCP(以字节流)、UDP(以数据包传输) |
网络层 | IP、ICMP、IGMP |
网络接口和物理层 | 以太网、令牌环网、FDDI |
TCP/IP协议分成两个不同的协议:
一、TCP与UDP
(一)TCP
1、 TCP协议特点
TCP协议(传输控制协议)特点:全双工的面向连接的传输层协议,它能提高可靠想通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。
2、TCP协议适用情况
(二)UDP
1、UDP协议特点
UDP(用户数据报协议):是不可靠的无连接协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
2、UDP协议适用情况
(三)TCP与UDP区别
共同点:同为传输层协议;
不同点:TCP - 有连接、可靠;
UDP - 无连接、不保证可靠、效率高;
二、socket
socket特点:
三、IP地址
(一)IP地址特点
(二)IP地址解析
IP地址分类:A(1+3)、B(2+2)、C(3+1)、D、E类,其中D、E类用于广播。
(三)IP地址的转换
1、inet_addr()
将点分形式的IP地址转换为网络IP地址(大端存储的无符号32位整形)。
#include
函数原型: in_addr_t inet_addr(const char *address);//返回转换后的地址
address是以NULL结尾的点分IPV4字符串。
返回32位的地址。如果字符串包含不合格法的IP地址,则函数返回-1;
struct in_addr addr;
addr.s_addr = inet_addr(192.168.1.122);
2、inet_aton()
#include
int inet_aton(const char *cp, struct in_addr *inp);
3、inet_ntoa()
#include
char *inet_ntoa(struct in_addr in);
in是IPV4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针。如果错误则函数返回NULL。
(四)字节序转换转换
网络中传输的数据必须按网络字节序,即大端字节序。
1、大小端
大端序:低字节存储在高低序;
小端序:低字节存储在高地址;
2、字节序转换函数
(1)主机字节序到网络字节序
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
(2)网络字节序到主机字节序
#include
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
一、服务器与客户端流程
二、通信相关函数
(一)socket
#include
#includeint socket(int domain, int type, int protocol);
domain是地址族:AF_INET --- IPv4 Internet protocols
AF_INET6 --- IPv6 Internet protocolsAF_UNIX --- unix协议,本地多进程间通信
type是套接字类型:SOCK_STREAM --- 流式套接字(TCP)
SOCK_DGRAM --- 数据报套接字(UDP)
SOCK_RAW --- 原始套接字
protocol --- 通常设为0
(二)bind
1、地址相关数据结构
(1)通用地址结构
struct sockaddr {
sa_family_t sa_family; //地址族,AF_xxx
char sa_data[14]; //14字节协议地址(存储服务器的IP地址和端口号)
};
(2)Internet协议地址结构
struct sockaddr_in struct sockaddr_un
{
u_short sin_family; //地址族,AF_INET,2bytes
u_short sin_port; 端口,2bytes =htons()
struct in_addr sin_addr; //IPV4地址,4bytes 见下面结构体
char sin_zero[8]; //8bytes unused,作为填充
};
用法:
/*定义一个 struct sockaddr_in类型的变量并清空*/
struct sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
/*填充地址信息*/
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(8888);//端口号
myaddr.sin_addr.s_addr = inet_addr("192.168.1.123");
(3) IPV4地址结构
struct in_addr
{
in_addr_t s_addr;
};
2、bind()
#include
#includeint bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd --- socket()返回的文件描述符
struct sockaddr --- 上面通用结构体,addr是指向sockaddr_in 结构的指针,包含本机IP地址和端口号
addrlen --- sockaddr地址结构的长度;sizof(struct sockaddr_in)
返回值:0、-1(出错)
(三)listen
功能:启动服务器,启动监听。启动监听之后,套接字由主动变为被动。
#include
#includeint listen(int sockfd, int backlog);
sockfd --- 监听连接的套接字
backlog --- 指定了正在等待连续的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
一般设其值为0
返回值:0或-1
(四)accept
#include
#includeint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd --- 监听套接字
addr --- 对方地址
addrlen --- 地址长度
socklen_t addrlen
addrlen = sizeof(socklen_t );
addrlen这个参数设为&addrlen
返回值:已建立好连接的套接字(文件描述符)或-1
(五)connect
#include
#includeint connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd --- socket()返回的文件描述符addr --- 服务器端的地址信息
addrlen --- addr的长度
(六)send
#include
#includessize_t send(int sockfd, const void *buf, size_t len, int flags);
buf --- 发送缓冲区首地址len --- 发送的字节数
flags --- 发送方式
(七)recv
#include
#includessize_t recv(int sockfd, void *buf, size_t len, int flags);
(八) 服务器和客户端代码示例
server.c
include */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
struct sockaddr_in srvaddr1;
socklen_t addrlen = sizeof(socklen_t) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("0.0.0.0");//调用本地IP地址
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
/*启动监听*/
ret = listen(socketfd,0);
if( ret == -1)
{
perror("server->listen->error");
return -1;
}
/*等待服务器的链接请求*/
connfd = accept(socketfd,(struct sockaddr *)&srvaddr1,&addrlen);
if( connfd == -1)
{
perror("server->accept->error");
return -1;
}
printf("connect success\n");
while(1)
{
printf("%s",inet_ntoa(srvaddr1.sin_addr));//获取客户端的地址
/*
ret = read(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("srvaddr->read->error");
return -1;
}
fputs(buf,stdout);
memset(buf,0,sizeof(sizeof(buf)));
fgets(buf,sizeof(buf),stdin);
ret = write(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("server->write->error");
return -1;
}
*/
memset(buf,0,sizeof(buf));
ret = recv(connfd,buf,sizeof(buf),0);
if( ret == -1)
{
perror("srvaddr->read->error");
return -1;
}
fputs(buf,stdout);
/*
memset(buf,0,sizeof(sizeof(buf)));
fgets(buf,sizeof(buf),stdin);
ret = send(connfd,buf,sizeof(buf),0);
if( ret == -1)
{
perror("server->write->error");
return -1;
}
*/
}
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int ret;
char buf[100];
int sockefd;
struct sockaddr_in cltaddr;
sockefd = socket(AF_INET,SOCK_STREAM,0);
/*建立客服端*/
if( sockefd == -1)
{
perror("client->sockefd->error");
return -1;
}
/*请求连接服务器*/
memset(&cltaddr,0,sizeof(struct sockaddr));
cltaddr.sin_family = AF_INET;
cltaddr.sin_port = htons(9696);
cltaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
ret = connect(sockefd,(const struct sockaddr *)&cltaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("cltaddr->connect->error");
return -1;
}
printf("connect is success\n");
memset(buf,0,sizeof(buf));
while(1)
{
/*
fgets(buf,sizeof(buf),stdin);
ret = write(sockefd,buf,sizeof(buf));
if( ret == -1)
{
perror("cltent->write->error");
return -1;
}
memset(buf,0,sizeof(buf));
ret = read(sockefd,buf,sizeof(buf));
fputs(buf,stdout);
*/
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
ret = send(sockefd,buf,sizeof(buf),0);
if( ret == -1)
{
perror("send");
return -1;
}
/* memset(buf,0,sizeof(buf));
ret = recv(sockefd,buf,sizeof(buf),0);
fputs(buf,stdout);
*/
}
return 0;
}
用进程来实现服务器与客户端,一个进程用于读数据,一个进程用于写数据:
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
***********************************************************************/
#include */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
socklen_t addrlen = sizeof(socklen_t) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
/*启动监听*/
ret = listen(socketfd,0);
if( ret == -1)
{
perror("server->listen->error");
return -1;
}
/*等待服务器的链接请求*/
connfd = accept(socketfd,(struct sockaddr *)&srvaddr,&addrlen);
if( connfd == -1)
{
perror("server->accept->error");
return -1;
}
printf("connect success\n");
while(1)
{
memset(buf,0,sizeof(buf));
pid_t pid1;
pid1 = fork();
if( pid1 == -1 )
{
perror("fork");
return -1;
}
else if( pid1 == 0 )
{
ret = read(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("srvaddr->read->error");
return -1;
}
fputs(buf,stdout);
}
else
{
memset(buf,0,sizeof(sizeof(buf)));
fgets(buf,sizeof(buf),stdin);
ret = write(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("server->write->error");
return -1;
}
}
}
return 0;
}
/************************************************************************
> 文件名称:client.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时57分49秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int ret;
char buf[100];
int sockefd;
struct sockaddr_in cltaddr;
sockefd = socket(AF_INET,SOCK_STREAM,0);
/*建立客服端*/
if( sockefd == -1)
{
perror("client->sockefd->error");
return -1;
}
/*请求连接服务器*/
memset(&cltaddr,0,sizeof(struct sockaddr));
cltaddr.sin_family = AF_INET;
cltaddr.sin_port = htons(9696);
cltaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = connect(sockefd,(const struct sockaddr *)&cltaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("cltaddr->connect->error");
return -1;
}
printf("connect is success\n");
memset(buf,0,sizeof(buf));
while(1)
{
memset(buf,0,sizeof(buf));
pid_t pid1;
pid1 = fork();
if( pid1 == -1 )
{
perror("fork");
return -1;
}
else if( pid1 == 0 )
{
fgets(buf,sizeof(buf),stdin);
ret = write(sockefd,buf,sizeof(buf));
if( ret == -1)
{
perror("cltent->write->error");
return -1;
}
}
else
{
memset(buf,0,sizeof(buf));
ret = read(sockefd,buf,sizeof(buf));
fputs(buf,stdout);
}
}
return 0;
}
UDP不需要建立连接,UDP服务器和客户端流程如下:
一、UDP的发送接收函数
(一)sendto()
#include
#includessize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);sockfd --- 套接字文件描述符
buf --- 缓冲区的首地址
len --- 发送数据的大小
flags --- 发送方式,默认设为0
addrlen --- 源地址空间大小
返回值 --- 成功:发送数据的字节数
失败: -1
(二) recvfrom()
#include
#includessize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
二、UDP实现服务器和客户端
server.c
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
socklen_t addrlen = sizeof(struct sockaddr) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_DGRAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
while(1)
{
memset(buf,0,sizeof(buf));
ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr *)&srvaddr,&addrlen);
if( ret == -1 )
{
perror("recvfrom");
return -1;
}
fputs(buf,stdout);
}
return 0;
}
client.c
9/************************************************************************
> 文件名称:client.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时57分49秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int ret;
char buf[100];
int sockefd;
struct sockaddr_in cltaddr;
sockefd = socket(AF_INET,SOCK_DGRAM,0);
socklen_t addrlen = sizeof(struct sockaddr);
/*建立客服端*/
if( sockefd == -1)
{
perror("client->sockefd->error");
return -1;
}
/*请求连接服务器*/
memset(&cltaddr,0,sizeof(struct sockaddr));
cltaddr.sin_family = AF_INET;
cltaddr.sin_port = htons(9696);
cltaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
ret = sendto(sockefd,buf,sizeof(buf),0,(struct sockaddr *)&cltaddr,addrlen);
if( ret == -1 )
{
perror("sendto");
return -1;
}
}
}
三、网络信息检索函数
1、getsockopt()/setsockopt()
#include
/* See NOTES */
#includeint getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);//获取一个套接口选项
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);//设置一个套接口选项sockfd --- 套接字
level --- 指定控制套接字的层次,可以取下列三种值:
SOL_SOCKET:通用套接字选项
IPPROTO_IP:IP选项
IPPROTO_TCP:TCP选项
optname --- 指定控制的方式(选项名称)
optval --- 获得或者是设置套接字选项。根据选项名称的数据类型进行转换
optlen --- 选项值的最大长度
如果同时发给局域网中的所有主机,称为广播;
只有用户数据报(UDP协议)套接字才能广播;
一、广播地址
二、广播接收
三、广播代码实现
server.c
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
socklen_t addrlen = sizeof(struct sockaddr) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_DGRAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.255");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
while(1)
{
memset(buf,0,sizeof(buf));
ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr *)&srvaddr,&addrlen);
if( ret == -1 )
{
perror("recvfrom");
return -1;
}
fputs(buf,stdout);
}
return 0;
}
client.c
/************************************************************************
> 文件名称:client.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时57分49秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int ret;
char buf[100];
int sockefd;
struct sockaddr_in cltaddr;
sockefd = socket(AF_INET,SOCK_DGRAM,0);
socklen_t addrlen = sizeof(struct sockaddr);
// socklen_t addrlen;
/*建立客服端*/
if( sockefd == -1)
{
perror("client->sockefd->error");
return -1;
}
printf("11\n");
/*请求连接服务器*/
int opt = 1;
ret = setsockopt(sockefd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));
printf("b\n");
if( ret == -1 )
{
perror("setsockopt");
return -1;
}
printf("c\n");
memset(&cltaddr,0,sizeof(struct sockaddr_in));
printf("d\n");
cltaddr.sin_family = AF_INET;
cltaddr.sin_port = htons(9696);
cltaddr.sin_addr.s_addr = inet_addr("192.168.2.22");
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
ret = sendto(sockefd,buf,sizeof(buf),0,(struct sockaddr *)&cltaddr,addrlen);
if( ret == -1 )
{
perror("sendto");
return -1;
}
}
}
一、加入组播
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
/*加入组播*/
struct ip_mreq mreq;
memset(&mreq,0,sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(socketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
二、组播代码实现
server.c
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
socklen_t addrlen = sizeof(struct sockaddr) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_DGRAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*加入组播*/
struct ip_mreq mreq;
memset(&mreq,0,sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(socketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
while(1)
{
memset(buf,0,sizeof(buf));
ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr *)&srvaddr,&addrlen);
if( ret == -1 )
{
perror("recvfrom");
return -1;
}
fputs(buf,stdout);
}
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("224.10.10.1");
while(1) {
fgets(buf, sizeof(buf), stdin);
ret = sendto(sockfd, buf, sizeof(buf), 0, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1) {
perror("sendto");
return -1;
}
printf("ret = %d\n", ret);
}
return 0;
}
在UNIIX/Linux下主要有4中I/O模型:阻塞I//O、非阻塞I/O、I/O多路复用、信号驱动IO(一种异步通信模型)。
一、阻塞I/O
1、特点:最常用、最简单、效率低。
2、以read函数为例进行解释
进程调用read函数从套接字上读取数据,当套接字的接收缓冲区中还没数据可读,函数read将发生阻塞,它一直阻塞下去,等待套接字的接收缓冲区有数据可读。当缓冲区内接收到数据,于是内核便去唤醒该进程,通过read访问这些数据。
int main()
{
char buf[128];
memset(&buf,0,sizeof(buf));
while(1)
{
read(0,buf,sizeof(buf));
printf("%s\n",buf);
}
return 0;
}
二、非阻塞I/O
1、特点
2、以fcntl为例
#include
#includeint fcntl(int fd, int cmd, ... /* arg */ );
#include
#include
#include
#include
int main()
{
int ret;
char buf[128];
int flag = fcntl(0,F_GETFL,0);
flag |= O_NONBLOCK;
fcntl(0,F_SETFL,flag);
while(1)
{
memset(buf,0,sizeof(buf));
ret = read(0,buf,sizeof(buf));
printf("buf = %s\n",buf);
sleep(1);
}
return 0;
}
三、I/O多路复用
允许同时对多个I/O进行控制,主要有三种机制:select()、poll()、epoll_ctl()。
select()、poll()、epoll_ctl()的思路:
while(1)
{
if(listenfd)
//说明是新的客户端发起了请求;
if(confd)
//说明是已经连接的客户端发送了数据请求;
if(/*普通文件*/)
//说明普通文件的数据;
if(0)
//标准输入;
}
(一)select
select机制思路及步骤:
(1)找到那些文件描述符;
(2)处理数据;
#include
#include
#includeint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds --- 最大文件描述符+1readfds --- 所有要读的文件描述符的集合(读文件件描述表)
writefds --- 所有要写的文件描述符的集合(写文件件描述表)
exceptfds --- 其他要向我们通知的文件描述(错误处理文件描述符表)
timeout --- 超时设置:NULL:一直阻塞,直到有文件描述符就绪或出错
0:仅仅检测文件描述符集的状态,然后立即返回
不为0:在指定时间内,如果没有事件发生,则超时返回
返回值:成功 --- 准备就绪的文件描述符的个数
失败 --- -1
超时返回 ---- 0
设置文件描述符的几个宏:
- void FD_CLR(int fd, fd_set *set); //将fd从集合set当中清除
- int FD_ISSET(int fd, fd_set *set); //判断fd是否在集合set中
- void FD_SET(int fd, fd_set *set); //将fd添加到集合set中
- void FD_ZERO(fd_set *set); //清空集合set
select实现并发服务器:
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
************************************************************************/
#include */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int select_func(int connfd)
{
char buf[100];
int ret;
memset(buf,0,sizeof(buf));
ret = read(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("srvaddr->read->error");
return -1;
}
if( ret == 0 )
{
return 1;
}
fputs(buf,stdout);
return 0;
}
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
int addrlen = sizeof(struct sockaddr_in ) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
/*启动监听*/
ret = listen(socketfd,1024);
if( ret == -1)
{
perror("server->listen->error");
return -1;
}
int fd;
int nfds;
/* 创建一个集合,用来存储所要读处理的所有的文件描述符*/
fd_set readfds;
fd_set rfds;
/* 添加要读处理的文件描述符 */
FD_SET(socketfd,&readfds);
nfds = socketfd + 1;
while(1)
{
printf("==========\n");
rfds=readfds;
/* 检测集合当中是否有继续的文件描述符 */
ret = select(nfds,&rfds,NULL,NULL,NULL);
/* 轮循 */
for(fd = 0;fd < nfds;fd++)
{
/* 判断,寻找就绪的文件描述符 */
if( FD_ISSET(fd,&rfds) )
{
/* 如果是监听套接字socketfd,则建立连接 */
printf("---------------\n");
if( fd == socketfd )
{
printf("++++++++++++++++\n");
memset(&srvaddr,0,sizeof(srvaddr));
connfd = accept(socketfd,(struct sockaddr *)&srvaddr,&addrlen);
if( -1 == connfd )
{
perror("accept");
return -1;
}
printf("connnect success connfd = %d\n",connfd);
FD_SET(connfd,&readfds);
if( connfd >= nfds )
{
nfds = connfd + 1;
}
}
else
{
ret = select_func(fd);
if( ret == 1 )
{
FD_CLR(fd,&readfds);
close(fd);
}
}
}
}
}
close(socketfd);
return 0;
}
(二)poll
poll机制思路及步骤:
(1)找到发生的事件;
(2)处理数据;
功能:检测是否有文件描述和事件处于就绪状态,有返回,没有一直阻塞。
#include
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds --- 事件和文件描述符的集合
nfds --- 最大文件描述符+1
timeout --- 超时时间(ms)
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 需要的事件*/
short revents; /* 返回的事件 */
};
poll()机制实现并发服务器:
/************************************************************************
> 文件名称:server.c
> 创建者:wl
> 邮箱:[email protected]
> 时间:2017年09月19日 星期二 14时15分50秒
> 描述:
***********************************************************************nclude */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int poll_func(int connfd)
{
char buf[100];
int ret;
memset(buf,0,sizeof(buf));
ret = read(connfd,buf,sizeof(buf));
if( ret == -1)
{
perror("srvaddr->read->error");
return -1;
}
if( ret == 0 )
{
return 1;
}
fputs(buf,stdout);
return 0;
}
int main (void)
{
int socketfd;
int ret;
char buf[100];
int connfd;
struct sockaddr_in srvaddr;
int addrlen = sizeof(struct sockaddr_in ) ;
/*创建socket套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if( socketfd == -1)
{
perror("server->socket->error");
return -1;
}
/*设置服务器的IP地址和段口号*/
memset(&srvaddr,0,sizeof(struct sockaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9696);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(socketfd,(const struct sockaddr *)&srvaddr,sizeof(struct sockaddr));
if( ret == -1)
{
perror("server->bind->error");
return -1;
}
/*启动监听*/
ret = listen(socketfd,1024);
if( ret == -1)
{
perror("server->listen->error");
return -1;
}
/* 创建集合,并且将集合当中的每一个数组元素的fd成员赋值为-1;*/
int nfds;
int fd;
int i,j;
struct pollfd fds[1024];
for(i=0;i<1024;i++)
{
fds[i].fd = -1;
}
/* 添加所需要处理的事件和文件描述符 */
fds[0].fd = socketfd;
fds[0].events = POLLIN;
nfds = socketfd + 1;
while(1)
{
/* 检测集合当中是否有继续的文件描述符 */
ret = poll(fds,nfds,5000);
if( ret == -1)
{
perror("poll");
return -1;
}
else if( ret == 0 )
{
printf("timeout\n");
continue;
}
/* 轮循 */
for( i=0;i<1024;i++)
{
/* 判断是什么事件 */
if( POLLIN == fds[i].events)
{
/* 判断,寻找就绪的文件描述符 */
if( fds[i].fd != -1 )
{
fd = fds[i].fd;
/* 如果是监听套接字listenfd,则建立连接 */
if( fd == socketfd )
{
memset(&srvaddr,0,sizeof(srvaddr));
connfd = accept(socketfd,(struct sockaddr *)&srvaddr,&addrlen);
if( connfd == -1 )
{
perror("accept");
return -1;
}
printf("connent success connfd = %d\n",connfd);
for( j=0;j<1024;j++)
{
if( fds[j].fd != -1 )
{
continue;
}
fds[j].fd = connfd;
fds[j].events = POLLIN;
if( nfds <= connfd )
{
nfds = connfd + 1;
}
}
}
else
{
ret = poll_func(fd);
if( ret == -1 )
{
fds[i].fd = -1;
}
}
}
}
}
}
close(socketfd);
return 0;
}
(三) epoll_ctl
#include
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);