废话不多数。先上代码。server.cpp
#include <jni.h>
#include <string>
#include <cmath>
#include <sys/epoll.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include "android/log.h"
#include "jni.h"
#include <pthread.h>
struct epoll_event ev, events[20];
#define LISTENQ 20
#define MAXLINE 5
int main(int argc, char *argv[]) {
int i, maxi, listenfd, connfd, sockfd, myepfd, nfds, portnumber;
ssize_t n;
char line[MAXLINE];
socklen_t clilen;
myepfd = epoll_create(58);
portnumber = 8888;
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
std::cout << "文件描述符为" + std::to_string(listenfd) << std::endl;
if (listenfd < 0) {
std::cout << "打开文件错误" + std::to_string(listenfd) << std::endl;
}
ev.data.fd = listenfd;
//设置要处理的事件类型
ev.events = EPOLLIN | EPOLLOUT;
epoll_ctl(myepfd, EPOLL_CTL_ADD, listenfd, &ev);
serveraddr.sin_family = AF_INET;
char *local_addr = "10.6.25.153";
inet_aton(local_addr, &(serveraddr.sin_addr));//htons(portnumber);
serveraddr.sin_port = htons(portnumber);
bind(listenfd, (sockaddr *) &serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ);
for (;;) {
//等待epoll事件的发生
int nfds = epoll_wait(myepfd, events, 20, -1);
std::cout << "addr(obj2.pszTestStr) is: " + std::to_string(nfds) << std::endl;
for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
{
connfd = accept(listenfd, (sockaddr *) &clientaddr, &clilen);
if (connfd < 0) {
std::cout << "connfd<0" << std::endl;
exit(1);
}
char *str = inet_ntoa(clientaddr.sin_addr);
std::cout << "accapt a connection from " << str << std::endl;
//设置用于读操作的文件描述符
ev.data.fd = connfd;
//设置用于注测的读操作事件
ev.events = EPOLLIN | EPOLLET;
//注册ev
epoll_ctl(myepfd, EPOLL_CTL_ADD, connfd, &ev);
} else if (events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。
{
std::cout << "EPOLLIN" << std::endl;
if ((sockfd = events[i].data.fd) < 0)
continue;
if ((n = read(sockfd, line, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
close(sockfd);
events[i].data.fd = -1;
} else
std::cout << "readline error" << std::endl;
} else if (n == 0) {
close(sockfd);
events[i].data.fd = -1;
}
line[n] = '/0';
std::cout << "read " << line << std::endl;
//设置用于写操作的文件描述符
ev.data.fd = sockfd;
//设置用于注测的写操作事件
ev.events = EPOLLOUT | EPOLLET;
//修改sockfd上要处理的事件为EPOLLOUT
epoll_ctl(myepfd, EPOLL_CTL_MOD, sockfd, &ev);
} else if (events[i].events & EPOLLOUT) // 如果有数据发送
{
sockfd = events[i].data.fd;
write(sockfd, line, n);
//设置用于读操作的文件描述符
ev.data.fd = sockfd;
//设置用于注测的读操作事件
ev.events = EPOLLIN | EPOLLET;
//修改sockfd上要处理的事件为EPOLIN
epoll_ctl(myepfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
}
return 0;
}
以上为server的代码,我们现在把它编译为能够运行在android手机上。这里要注意下。监听的ip要为自己手机的实际ip地址。可以通过ifconfig查看。如果用本机的“127.0.0.1”会监听不成功。利用android studio的cmake编译系统能够方便的编译。这里我把我的cmakeList贴出来。
add_executable(
server
server.cpp
)
另外app的build.gradle 文件中,配置支持c++。
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
}
实际上主要就是这一句话。现在看下效果。
首先看下服务运行。注意server文件要不能拷贝到sdrcard这类目录运行,因为没有运行权限。
然后另外一台电脑通过telnet 可以进行socket通信。
输入字符对应android服务器就可以读取出来了
大家可以看到,连接建立的时候epoll_wait 会返回,在epoll_event 中我们能够相应的事件。具体epoll的实现原理不细讲了,大家最好还是通过代码来熟悉相关原理比较好。