IO多路复用实现TCP服务器
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
//IO多路复用实现TCP服务器
#define ERR_MSG(msg) do{\
fprintf(stderr,"line :%d",__LINE__);\
perror("msg");\
}while(0)
#define IP "192.168.0.105"
int main(int argc, const char *argv[])
{
//创建套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速被重复使用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速复用");
//填充自身地址信息结构体
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(6666);
my_addr.sin_addr.s_addr = inet_addr(IP);
//定义地址信息结构体数组存储接收的地址信息
struct sockaddr_in peer_addr[128-4];
struct sockaddr_in ls_addr;
socklen_t addrlen = sizeof(ls_addr);
ssize_t res = -1;
//绑定地址信息结构体
res = bind(sfd,(struct sockaddr *)&my_addr,sizeof(my_addr));
if(res < 0)
{
ERR_MSG("bind");
return -1;
}
printf("success to bind, line:%d\n",__LINE__);
//改为被动监听模式
int backlog = 128;
res = listen(sfd,backlog);
if(res < 0)
{
ERR_MSG("listen");
return -1;
}
printf("success to listen, line:%d\n",__LINE__);
//正常应该接收客户端的信号,但是为了一个进程、线程实现多个客户端处理
//需要调用select函数,对不同文件标识符 是否 接收到信号进行判断
//将对应函数的阻塞变成对select的阻塞,一旦select接收到信号,
//代表对应的文件标识符处理函数就绪,可以运行
//select函数需要文件标识符集合,定义一个该集合
fd_set readfds,tempfds;
//清空集合
FD_ZERO(&readfds);
//将文件标识符放入集合
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int maxfd = sfd; //定义最大文件标识符并初始化
int nfd = -1; //定义随机文件标识符
int newfd = -1; //定义新的文件标识符
int i = 0; //循环变量
char buf[128] = ""; //定义字符数组
int s_res = 0;
while(1)
{
tempfds = readfds; //tempfds代替readfds.防止数据丢失
s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
//参数1:文件标识符集合中,最大的文件标识符+1
//参数2:输入文件标识符集合
//3.4.输出,其他文件标识符集合,基本用不到
//5.设定的超时时间
if(s_res < 0)
{
ERR_MSG(select);
return -1;
}else if(0 == s_res)
{
printf("time out..\n");
break;
}
printf("line:%d\n",__LINE__);
//对信号进行处理
for(i = 0;i <= maxfd ;i++) // i此时就是文件标识符
{
//若没有此标识符
if(0 == FD_ISSET(i,&tempfds))
continue;
//若标识符是0
if( 0 == i)
{
printf("请输入客户端标识符:");
scanf("%d",&nfd);
getchar();
//对文件标识符进行判断
if(!FD_ISSET(nfd,&readfds) ||nfd<sfd||nfd>128)
{
fprintf(stderr,"非法文件标识符");
continue;
}
printf("请输入信息:");
scanf("%s",buf);
getchar();
//发送信息给客户端
if(send(nfd,buf,sizeof(buf),0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("success to send, line:%d\n",__LINE__);
}else if(sfd == i)
{
//代表监听到客户端的连接请求
printf("触发客户端连接事件\n");
newfd = accept(sfd,(struct sockaddr *)&ls_addr,&addrlen);
if(newfd < 0)
{
ERR_MSG("accept");
return -1;
}
printf("[%s,%d],newfd = %d,客户端连接,line:%d\n",inet_ntoa(ls_addr.sin_addr),\
ntohs(ls_addr.sin_port),newfd, __LINE__);
peer_addr[newfd-4] = ls_addr;
//将文件标识符放入集合
FD_SET(newfd,&readfds);
//更新maxfd
maxfd = maxfd > newfd ? maxfd : newfd ;
}else
{
printf("触发客户端交互事件\n");
bzero(buf,sizeof(buf));
res = recv(i,buf,sizeof(buf),0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}else if(0 == res)
{
printf("[%s,%d],newfd = %d,客户端下线,line:%d\n",inet_ntoa(peer_addr[i-4].sin_addr),\
ntohs(peer_addr[i-4].sin_port),i, __LINE__);
close(i);
FD_CLR(i,&readfds);
int j = maxfd;
for(;j>= 0;j--)
{
if(FD_ISSET(j,&readfds))
break;
}
maxfd = j;
continue;
}
printf("[%s:%d] newfd = %d %s __%d__\n",inet_ntoa(peer_addr[i-4].sin_addr),\
ntohs(peer_addr[i-4].sin_port),i,buf , __LINE__);
}
}
}
//关闭文件描述符
close(sfd);
return 0;
}
IO多路复用实现客户端(poll)
#include<stdio.h>
#include <sys/types.h>
#include<string.h>
#include<stdlib.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<unistd.h>
#include<poll.h>
//客户端
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d\n",__LINE__);\
perror("msg");\
}while(0)
#define IP "192.168.0.198"
int main(int argc, const char *argv[])
{
//创建流式套接字
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//绑定----非必须绑定 IP地址和端口(1024-49151) 绑定到套接字
//如果不绑定,操作系统自行绑定
//地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; /* address family: AF_INET */
sin.sin_port = htons(6666); ; /* port in network byte order */
sin.sin_addr.s_addr = inet_addr(IP); //ip,本机IP ifcofig
//连接指定服务器
int nect = connect(cfd,(struct sockaddr *)&sin,sizeof(sin));
if(nect < 0)
{
ERR_MSG("connect");
return -1;
}
printf("success to connect\n");
struct pollfd fds[2];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = cfd;
fds[1].events = POLLIN;
char buf[128] = "";
ssize_t res = 0;
ssize_t snd = 0;
int p_res = -1;
while(1)
{
p_res = poll(fds,2,-1);
if(p_res < 0)
{
ERR_MSG("poll");
return -1;
}else if(0 == poll)
{
printf("time out..\n");
break;
}
if(fds[0].revents&POLLIN)
{
printf("触发键盘输入事件\n");
bzero(buf,sizeof(buf)); //清空字符串
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
//发送
snd = send(cfd,buf ,sizeof(buf),0);
if(snd < 0)
{
ERR_MSG("send");
return -1;
}
printf("success to send\n");
}
if(fds.revents&POLLIN)
{
printf("触发服务器交互事件\n");
bzero(buf,sizeof(buf)); //清空字符串
//接收
res = recv(cfd ,buf, sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(res == 0)
{
printf("服务器下线\n");
}
printf("%s\t__%d__\n",buf,__LINE__);
}
}
//关闭文件描述符
close(cfd);
return 0;
}
IO多路复用实现TCP客户端
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
//客户端
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d\n",__LINE__);\
perror("msg");\
}while(0)
#define IP "192.168.0.198"
int main(int argc, const char *argv[])
{
//创建流式套接字
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//绑定自身地址信息----非必须绑定 IP地址和端口(1024-49151) 绑定到套接字
//如果不绑定,操作系统自行绑定
//服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; /* address family: AF_INET */
sin.sin_port = htons(6666); ; /* port in network byte order */
sin.sin_addr.s_addr = inet_addr(IP); //ip,本机IP ifcofig
fd_set readfds , tempfds;
FD_ZERO(0,&readfds);
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
//连接指定服务器
int nect = connect(cfd,(struct sockaddr *)&sin,sizeof(sin));
if(nect < 0)
{
ERR_MSG("connect");
return -1;
}
printf("success to connect\n");
char buf[128] = "";
ssize_t res = 0;
ssize_t snd = 0;
int s_res = 0;
while(1)
{
tempfds = readfds ;
s_res = select(fds+1,&tempfds,NULL,NULL,NULL);
if(s_res < 0)
{
ERR_MSG(select);
return -1;
}else if(0 == s_res)
{
printf("time out..");
return -1;
}
printf("line:%d\n",__LINE__);
if(FD_ISSET(cfd,&tempfds))
{
printf("触发服务器交互事件\n");
bzero(buf,sizeof(buf)); //清空字符串
//接收
res = recv(cfd ,buf, sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(res == 0)
{
printf("服务器下线\n");
}
printf("%s\t__%d__\n",buf,__LINE__);
}
if(FD_ISSET(0,&tempfds))
{
printf("触发键盘输入事件\n");
bzero(buf,sizeof(buf)); //清空字符串
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
//发送
snd = send(cfd,buf ,sizeof(buf),0);
if(snd < 0)
{
ERR_MSG("send");
return -1;
}
printf("success to send\n");
}
}
//关闭文件描述符
close(cfd);
return 0;
}