客户端
// 三次握手主要是创建连接
// 四次挥手主要是释放资源
// I/O 复用 epoll模式
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h> // bzero()
#include <thread>
//#include <sys/select.h>
#include <sys/epoll.h> // epoll()
#include <linux/fs.h> // OPEN_MAX
using namespace std;
string name;
void show_connect(int fd) {
// 获取本地地址和端口
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len); // 清空 防止后面误用
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
// 获取远程地址和端口
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
// ./clinet IP port name
int main(int argc,char* argv[]) {
if(4!=argc) {
cout << "Usage:" << argv[0] << " IP port name" << endl;
return 1;
}
name = argv[3];
// 1.创建连接套接字
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd) {
cout << "socket error" << endl;
return 1;
}
// 2. 连接服务器
struct sockaddr_in remote_addr; // in是internet简写
remote_addr.sin_family = AF_INET; // 协议 sin是sockaddr_in的缩写
remote_addr.sin_addr.s_addr = inet_addr(argv[1]); // IP地址
remote_addr.sin_port = htons(atoi(argv[2])); // 端口号 小端转大端
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))) {
cout << "connect error" << endl;
return 1;
} else {
cout << "connect success" << endl;
cout << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
show_connect(connfd);
}
// 创建epoll描述符
int epollfd = epoll_create(2);
// 注册事件
struct epoll_event evt;
evt.data.fd = STDIN_FILENO;
evt.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt);
evt.data.fd = connfd;
evt.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);
bool stop = false;
while(!stop) {
int count = 2;
struct epoll_event revt[2];
int revt_count = epoll_wait(epollfd,revt,count,-1);
for(int i=0;i<revt_count;++i){
if(revt[i].data.fd == STDIN_FILENO && revt[i].events & EPOLLIN){
// 3.发送信息
string message;
getline(cin,message);
message = name + ":" + message;
write(connfd,message.c_str(),message.size()+1);
}else if(revt[i].data.fd == connfd && revt[i].events & EPOLLIN){
// 4.接收数据
char buffer[1024] = {0};
int len = read(connfd,buffer,sizeof(buffer));
if(len == 0) {
cout << "server exit" << endl;
epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
--count;
stop = true;
break;
} else {
cout << buffer<< endl;
}
}
}
}
close(epollfd);
// 5. 关闭套接字
close(connfd);
return 0;
}
服务端
// 一服务器 -> 多客户端
// 三次握手主要是创建连接
// 四次挥手主要是释放资源
// I/O复用 epoll模式
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h> // bzero()
#include <pthread.h>
#include <list> // remove()
#include <thread>
#include <algorithm> // max_element()
#include <sys/epoll.h> // epoll()
#include <linux/fs.h> // OPEN_MAX
using namespace std;
void show_connect(int fd) {
// 获取本地地址和端口
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len); // 清空 防止后面误用
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
// 获取远程地址和端口
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
// ./server ip port
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << "输入 IP port" << endl;
return 1;
}
// 1. 监听套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == listenfd) {
cout << "listen socket error" << endl;
return 1;
}
// 为了避免端口被占用,想要再次使用同一个端口
// 设置端口重复利用(一般用在调试中)
int flag = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
// 2. 绑定
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET; // IPv4协议
local_addr.sin_addr.s_addr = inet_addr(argv[1]); // IP地址
local_addr.sin_port = htons(atoi(argv[2])); // 端口号
if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) { //套接字和端口绑定
cout << "bind error" << endl;
return 1;
} else {
cout << "bind success" << endl;
}
// 3. 监听设置
if(-1==listen(listenfd,10)) {
cout << "listen error" << endl;
return 1;
} else {
cout << "listen success" << endl;
}
int epollfd = epoll_create(INR_OPEN_MAX);
struct epoll_event evt;
evt.data.fd = STDIN_FILENO;
evt.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt); //监听标准输入
evt.data.fd = listenfd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&evt);
int count = 2;
list<int> fds;
while(true) {
struct epoll_event revt[count];
int revt_cnt = epoll_wait(epollfd,revt,count,-1);
for(int i=0; i<revt_cnt; ++i) {
if(revt[i].data.fd == STDIN_FILENO && revt[i].events & EPOLLIN) { //如果事件是输入 并且文件描述符包括可读
string message;
getline(cin,message);
cin.clear(); // 清空输入出错
if(!message.empty()) { // 判断终端输入是否为空
message = "广告:" + message;
for(auto fd:fds) {
write(fd,message.c_str(),message.size()+1);
}
}
}else if(revt[i].data.fd == listenfd && revt[i].events & EPOLLIN) {
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr)); // 清空
socklen_t remote_addr_len = sizeof(remote_addr);
int connfd = accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
//int connfd = accept(listenfd,NULL,NULL);
if(-1 == connfd) {
cout << "accept error" << endl;
return 1;
} else {
cout << "accept success" << endl;
cout << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
show_connect(connfd);
//fds.push_back(connfd);
evt.data.fd = connfd;
evt.events = EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);
fds.push_back(connfd);
++count;
}
} else {
int connfd = revt[i].data.fd;
char buffer[1024] = {0};
int n = read(connfd,buffer,1024); // 读取客户端发过来的信息
if(n == 0) {
cout << "client exit" << endl;
close(connfd); // 关掉推出的连接
epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
fds.remove(connfd);
--count;
break;
} else {
for(auto fd:fds) { // 发送信息给所有的客户端
if(fd == connfd) continue;
write(fd,buffer,1024);
}
}
}
}
}
close(epollfd);
// 7. 关闭套接字
close(listenfd);
}