五种IO模型原理:
https://blog.csdn.net/ZWE7616175/article/details/80591587
https://www.cnblogs.com/javalyy/p/8882066.html
普通tcp和udp使用就是同步阻塞,recv()、recvfrom()等函数就是阻塞死等;
这里主要是 套接字的非阻塞和tcp的多路复用,udp天生具有并发,就不画蛇添足了。
客服端代码以及头文件comm.h见另一篇网络编程
UDP非阻塞服务器代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "comm.h"
int main(void)
{
int flags;
int ret;
int serv_socket;
struct sockaddr_in serv_addr;
struct sockaddr cli_addr;
socklen_t addrlen;
struct msg msg;
serv_socket = socket(AF_INET,SOCK_DGRAM, 0);
if(serv_socket < 0 ){
perror("udp serv socket err");
return -34;
}
flags = fcntl(serv_socket,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(serv_socket,F_SETFL,flags);
/*如何让 socket/fd 编程非阻塞呢
1.读取 socket里面的 flags
flags = fcntl(socket,F_GETFL,0);
2.将flags对应的bit置一
flags |= O_NONBLOCK;
3.将flags写入socket文件内部
fcntl(socket,F_SETFL,flags);
*/
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(3100);
serv_addr.sin_addr.s_addr = INADDR_ANY; //给一个0,bind发现 ip为0,则自动获取主机ip地址
ret = bind(serv_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(ret <0){
perror("bind err");
return -34;
}
while(1){
/*
非阻塞 返回值的问题
1.正确收到数据 ret>0
2.没有数据 ret<0 errno==EAGAIN
3.出错 ret<0
*/
loop:
memset(&msg,0,sizeof(msg));
addrlen = sizeof(cli_addr);
ret = recvfrom(serv_socket, &msg, sizeof(msg), 0,&cli_addr, &addrlen);
if(ret < 0){
if(errno == EAGAIN){
printf("recv current nodata,try next\n");
void do_something_other(void);
sleep(1);
goto loop;
}
printf("recvfrom err%d\n",ret);
return -34;
}
printf("serv get msg: type%d sval%d ival%d des:%s\n",
msg.type,msg.sval,msg.ival,msg.des);
msg.type++;
msg.sval++;
ret = sendto(serv_socket,&msg,sizeof(msg),0,&cli_addr,sizeof(cli_addr));
if(ret < 0){
printf("sendto err%d\n",ret);
return -34;
}
}
}
tcp的多路复用服务器代码
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
/* According to earlier standards */
#include
#include
#include "comm.h"
/*
void FD_CLR(int fd, fd_set *set); 将fd从set清除
int FD_ISSET(int fd, fd_set *set); 判断fd是否存在集合中
void FD_SET(int fd, fd_set *set); 将fd加入到set
void FD_ZERO(fd_set *set); 清空set
阻塞式的监控
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds:
集合中所有fd的 最大值+1
readfds: 可读事件,表示 当前可读fd的 一个集合
如果你想监控 某些socket/fd 的可读事件,那么 就将他们加入到该集合
双向参数: 你在调用之前,将需要监控的所有fd都加进去
等select返回的时候,里面只保存了 发生事件的fd
所以, 你需要备份
writefds: 可写...................
如果你想监控 .....................可写.................
双向参数: 你在调用之前,将需要监控的所有fd都加进去
等select返回的时候,里面只保存了 发生事件的fd
所以, 你需要备份
exceptfds:异常事件,表示 fd发生异常的集合
如果你想监控 异常事件 ............................
双向参数: 你在调用之前,将需要监控的所有fd都加进去
等select返回的时候,里面只保存了 发生事件的fd
所以, 你需要备份
timeout: 指定等待的最大时间,如果在改时间内 没有 任何fd发生事件,
返回
struct timeval {
long tv_sec;
long tv_usec;
};
==NULL, 永久监控
==非阻塞监控timeout->tv_sec=timeout->tv_usec = 0;
返回值:
-1: 错误
正值: 发生事件的fd的个数
0: 超时了
*/
struct sockaddr_in serv_local;
struct sockaddr_in cli_addr;
size_t addr_sz;
int socket_serv;
int socket_client;
fd_set readfds; //读集合
fd_set readfds_backup; //读集合
int fd_max;
struct timeval timeout;
int fd_client[128];
int fd_client_cnt = 0;
int serv_for_client(int socket_client)
{
int ret;
struct msg_type msg;
memset(&msg,0,sizeof(msg));
ret = recv(socket_client,&msg,sizeof(msg),0);
if(ret <0){
perror("recv err");
close(socket_client);
return -35;
}else if(ret == 0){
printf("server detect client over\n");
close(socket_client);
return -25;
}
printf("serv get msg:type%d tmp%d himmdy%d des: %s\n",
msg.msg_type,ntohs(msg.tmp),ntohl(msg.himmdty),msg.des);
msg.tmp = ntohs(msg.tmp);
msg.himmdty = ntohl(msg.himmdty);
msg.tmp++; //主机字节序
msg.himmdty += 3;
strcpy(msg.des,"hello from serv");
msg.tmp = htons(msg.tmp);
msg.himmdty = htonl(msg.himmdty);
ret = send(socket_client, &msg, sizeof(msg),0);
if(ret <0){
perror("send err");
close(socket_client);
return -36;
}
return 0;
}
int main(int argc,char **argv)
{
int ret;
int idx;
socket_serv = socket(AF_INET,SOCK_STREAM, 0);
if(socket_serv <0 ){
perror("server socket err");
return -12;
}
serv_local.sin_family = AF_INET;
serv_local.sin_port = htons(5050);
serv_local.sin_addr.s_addr = INADDR_ANY; /*INADDR_ANY = 0 代码自动绑定本机ip地址*/
ret = bind(socket_serv,(struct sockaddr *)&serv_local,sizeof(serv_local));
if(ret <0){
perror("bind err");
return -13;
}
ret = listen(socket_serv,5);
if(ret <0){
perror("listen err");
return -14;
}
/*当前有一个socket,那么 加入到监控集合中去
*/
FD_ZERO(&readfds);
FD_SET(socket_serv,&readfds);
fd_max = socket_serv;
loop:
timeout.tv_sec = 5;
timeout.tv_usec = 0;
readfds_backup = readfds;
ret = select(fd_max+1,&readfds_backup,NULL,NULL,&timeout);
if(ret <0){
perror("select err");
return -23;
}else if(ret == 0){
printf("select timeout, next time\n");
goto loop;
}
/*readfds_backup 里面保存了发生事件的fd,遍历*/
if(FD_ISSET(socket_serv,&readfds_backup)){ //有新的客户端发起连接
memset(&cli_addr,0,sizeof(cli_addr));
addr_sz = sizeof(cli_addr);
socket_client = accept(socket_serv,(struct sockaddr *)&cli_addr,&addr_sz);
if(socket_client < 0){
perror("serv accept err");
return -34;
}
printf("serv get client ip %s port %d\n",
inet_ntoa(cli_addr.sin_addr) ,ntohs(cli_addr.sin_port));
/*将新的套接字 加入监控空集合*/
FD_SET(socket_client,&readfds);
fd_max = fd_max > socket_client ? fd_max : socket_client;
fd_client[fd_client_cnt++] = socket_client;
}
for(idx=0;idx