一、实验目的和任务
- 本实验要求复习ubuntu的IP配置知识。
- 复习使用socket编程的基本方法。
- 学习epoll服务模式的基本用法。
二、实验设备介绍
1.软件需求: win7操作系统,VMware workstation,ubuntu12 [配置交叉编译环境]。
2.硬件需求: PC内存大于1G,硬盘空间大于20G;smart210开发板。
三、注意事项和要求
1.启动虚拟机配置IP地址。
2.注意启动多个客户端与服务端进行通信。
3.程序运行结果截图后放入实验报告中。
四、实验内容和步骤
epoll模式下操作系统负责保存监视对象文件描述符,epoll_wait函数等待文件描述符的变化,并且epoll方式将发生变化的文件描述符专门集中在一起,这样能大大提高程序执行效率,并降低资源消耗。
4.1回声服务端程序epoll_serv.c
//037.c《TCP/IP网络编程》page 279 epoll_serv.c 对应的客户端为 echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#define BUF_SIZE 100
#define EPOLL_SIZE 50
void setnonblockingmode(int fd);
void error_handling(char * message);
int main(int argc,char *argv[])
{
int server_sock,clnt_sock;
struct sockaddr_in serv_addr,clnt_adr;
socklen_t adr_sz;
int str_len,i;
char buf[BUF_SIZE];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd,event_cnt;
if(argc!=2)
{
printf("Usage :%s <PORT>\n",argv[0]);
exit(1);
}
server_sock=socket(PF_INET,SOCK_STREAM,0);
if(server_sock==-1)
error_handling("socket() create failed.");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
//IP地址
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//PORT
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(server_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
error_handling("bind() failed.");
if(listen(server_sock,5)==-1)
error_handling("listen() failed.");
epfd=epoll_create(EPOLL_SIZE);
//分配EPOLL_SIZE个epoll_event用于记录事件
ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
printf("ep_events=malloc(). \n");
//设置socket为非阻塞模式
setnonblockingmode(server_sock);
event.events=EPOLLIN;
event.data.fd=server_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,server_sock,&event);
while(1)
{
event_cnt=epoll_wait(epfd,ep_events,EPOLL_SIZE,500);
if(event_cnt==-1)
{
puts("epoll_wait() error.");
break;
}
//printf("return epoll_wait(). event_cnt=%d\n",event_cnt);
//遍历所有ep_events对象
for(i=0;i<event_cnt;i++)
{
//是监听socket对象事件则执行accept方法
if(ep_events[i].data.fd==server_sock)
{
adr_sz=sizeof(clnt_adr);
//每次accept会创建一个新socket用于数据通信
clnt_sock=accept(server_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
printf("connected client ID=: %d \n",clnt_sock);
//设置socket为非阻塞模式
setnonblockingmode(clnt_sock);
event.events=EPOLLIN|EPOLLET;
event.data.fd=clnt_sock;
//注册监视对象文件描述符
epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
}else
{//是普通socket对象则执行读操作
str_len=read(ep_events[i].data.fd,buf,BUF_SIZE);
if(str_len==0)
{//收到字节长度为0,表示对方shutdown,则执行close操作
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("closed client:%d \n",ep_events[i].data.fd);
break;
}else if(str_len<0)
{//收到字节长度小于0
if(errno==EAGAIN) break;
}else
{//收到字节度大于0,执行回复操作
printf("Message from %d. :%s",ep_events[i].data.fd,buf);
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
close(server_sock);
close(epfd);
return 0;
}
void setnonblockingmode(int fd)
{
int flag=fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flag|O_NONBLOCK);
}
//错误处理是将信息输出到标准输出,然后退出进程。
void error_handling(char * message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
下面是epoll模式运行示意图。
4.2回声客户端程序echo_client.c
为方便与服务端配合运行,将回声客户端程序在此列出,通过启动多个客户端实例来验证并发服务器功能。
// 053.c回声客户端《TCP/IP网络编程》page75
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc,char * argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_adr;
if(argc!=3)
{
printf("Usage : %s <IP> <port>\n",argv[0]);
exit(1);
}
//创建TCP客户端socket文件资源
sock=socket(PF_INET,SOCK_STREAM,0);
if(sock==-1)
error_handling("socket() 创建失败.");
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
serv_adr.sin_port=htons(atoi(argv[2]));
//连接服务端
if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
error_handling("connect() failed.");
else//连接成功
puts("Connected success .....");
while(1)
{
fputs("Input message(Q to quit):",stdout);
fgets(message,BUF_SIZE,stdin);
if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))
break;//收到q字符则退出
write(sock,message,strlen(message));
str_len=read(sock,message,BUF_SIZE-1);
message[str_len]=0;
printf("Message from server: %s",message);
}
close(sock);
return 0;
}
//错误处理是将信息输出到标准输出,然后退出进程。
void error_handling(char * message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}