我承认这篇文章有点标题党,呵呵。其实就是一个能和服务器建立全双工通信的客户端而已,用epoll机制实现。
上代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
//注意,不能一次性输入超过MAXLINE个的字符,否则程序会出错
#define MAXLINE 1024
//发生了致命错误,输出错误后立即退出
void error_quit(const char *str)
{
perror(str);
exit(1);
}
//关闭连接,退出程序
void close_connect(int sockfd)
{
close(sockfd);
printf("The connect have shutdown\n");
exit(0);
}
int main(int argc, char **argv)
{
int epfd, sockfd, nfs, tfd;
int readn, i, res;
struct sockaddr_in servaddr;
struct epoll_event ev, events[256];
char buffer[MAXLINE];
if( argc != 3 )
error_quit("usage: mytelnet <Address> <Port>");
//创建epoll事件处理机
epfd = epoll_create(256);
if( -1 == epfd )
error_quit("epoll_create error");
//创建用于TCP协议的套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons( atoi(argv[2]) );
res = inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
//连接服务器
if( res != 1 )
error_quit("inet_pton error");
res = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if( res < 0 )
error_quit("connect error");
//注册socket事件
ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
if( res < 0 )
error_quit("epoll_ctl error");
//注册标准输入事件
ev.data.fd = STDIN_FILENO;
ev.events = EPOLLIN|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
if( res < 0 )
error_quit("epoll_ctl error");
//开始事件循环
while( 1 )
{
nfs = epoll_wait(epfd, events, 20, 500);
for(i=0; i<nfs; i++)
{
if(events[i].events & EPOLLIN)
{
tfd = events[i].data.fd;
memset(buffer, 0, MAXLINE);
readn = read(tfd, buffer, MAXLINE);
//有网络消息传来了
if( sockfd == tfd )
{
if ( readn < 0)
{
// Connection Reset:你连接的那一端已经断开了,
//而你却还试着在对方已断开的socketfd上读写数据!
if (errno == ECONNRESET)
close_connect(sockfd);
else
error_quit("read error");
}
//连接正常关闭
else if ( readn == 0 )
close_connect(sockfd);
else
printf("%s", buffer);
}
//标准输入
if( STDIN_FILENO == tfd )
{
ev.data.fd = sockfd;
ev.events = EPOLLOUT|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
if( -1 == res )
error_quit("epoll_ctl error");
}
}
//如果有数据发送
else if(events[i].events & EPOLLOUT)
{
sockfd = events[i].data.fd;
write(sockfd, buffer, readn);
//注册用于读操作的文件描述符和事件
ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET;
res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
if( -1 == res )
error_quit("epoll_ctl error");
}
}
}
return 0;
}
编译与运行命令:
gcc mytelnet.c -o mytelnet -levent
./mytelnet 127.0.0.1 8877
用于测试的服务器:http://blog.csdn.net/aaa20090987/article/details/8769585