网络编程第三天
一.网络编程四种IO模型
1.阻塞IO(同步IO)
read(),recv(),recvfrom() 这些函数本身是不阻塞的
(1)是open(),socket(),accept() 这些函数导致阻塞的,系统会默认将open(),socket(),accept()设置为阻塞
(2)UDP协议中套接字写sendto…操作是不会发生阻塞的
2.非阻塞IO
1.给文件描述符设置非阻塞属性 —>缺点 : 占用cpu资源 2.由于是非阻塞属性,所以需要用户不断的轮询,检测数据是否到达
3.多路复用
1.同时对多个IO进行操作 2.可以设置超时属性
4.信号驱动(异步IO)
1.是属于异步IO,不需要等待 2.当客户端接入,会触发对应信号
//-------------------------------
二.阻塞IO与非阻塞IO的差异
1.阻塞IO 建立套接字(阻塞,系统默认)—>读取数据—》判断缓冲区有没有数据—》没有数据—》阻塞进入休眠状态 有数据—》读取数据
2.非阻塞IO 建立套接字(阻塞,系统默认)—>设置非阻塞属性套接字—》读取数据—》判断缓冲区有没有数据–》没有数据–》读取失败–》接口返回 有数据----》读取数据
3.设置非阻塞属性的套接字 man 2 fcntl #include <unistd.h> #include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
参数:
fd : 需要设置属性的文件描述符
cmd : 指定选择文件描述符的的设置方式
F_GETFL (void) : //获取文件描述符状态 成功返回文件的状态,失败返回 -1
F_SETFL (int) : // 设置文件描述符状态,此状态需要第三个参数arg
arg : 是否接收此参数是由cmd参数决定
O_APPEND, :---》文件追加属性
O_ASYNC, : ---》信号触发模式
O_DIRECT, :---》不使用缓冲区写入
O_NOATIME ---》不更新文件的修改时间
O_NONBLOCK --->非阻塞属性
例子 : int fd = socket() // 获取文件属性 int state = fcntl(fd,F_GETFL); // 将非阻塞属性更新到文件状态 state |= O_NONBLOCK; //int state1 = state | O_NONBLOCK; // 将新的状态重新设置回文件描述符 fcntl(fd,F_SETFL,state);
fcntl()返回值是根据文件描述符的设置方式来决定的
F_GETFL (void) : //获取文件描述符状态 成功返回文件的状态,失败返回 -1
F_SETFL (int) : // 设置文件描述符状态,此状态需要第三个参数arg
失败 -1
练习: 编写一个服务器需要监听等待客户端连接,如果有客户端添加进行,就将已连接的套接字放到数组 我们只能最多接收20个客户端,然后接收所有客户端发送过来的消息并打印出来
思路 : 1.将socket()设置为非阻塞,会导致accept()不发送阻塞,可以源源不断的接收客户端的connfd,将这个connfd 放到数组,并记录连接的个数
2.将accept()的返回值也设置为非阻塞,recv会导致非阻塞 struct client { int connfdbuf[20]; int count; }
三、阻塞和非阻塞IO
1.2 服务端代码
Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct client
{
int client_fd[20];
int count;
}client;
void init_client(client *mc_client)
{
for(int i =0; i < 20;i++)
{
(mc_client->client_fd)[i] = -1;
}
mc_client->count = 0;
}
int init_tcp(int port)
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("open socket failed ");
return -1;
}
int state = fcntl(sockfd,F_GETFL);
state |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,state);
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(saddr);
int ret = bind(sockfd, (struct sockaddr *)&saddr, len);
if(ret == -1)
{
perror("socket bind failed ");
return -1;
}
listen(sockfd,20);
return sockfd;
}
bool add_client(client *mc_client,int connfd)
{
if(mc_client->count > 20)
return false;
mc_client->client_fd[mc_client->count++] = connfd;
return true;
}
int main(int argc,char **argv)
{
if(argc != 2)
{
printf("传递端口号\n");
return -1;
}
client * mc_cilent = malloc(sizeof(client));
if(mc_cilent == NULL)
{
perror("malloc failed : ");
return -1;
}
init_client(mc_cilent);
int port = atoi(argv[1]);
int sock_fd = init_tcp(port);
if(sock_fd == -1)
{
return -1;
}
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
bzero(&clientaddr,len);
char *client_ip = NULL;
int connfd;
while(1)
{
connfd = accept(sock_fd, (struct sockaddr *)&clientaddr, &len);
if(connfd > 0)
{
printf("连接成功\n");
int state = fcntl(connfd,F_GETFL);
state |= O_NONBLOCK;
fcntl(connfd,F_SETFL,state);
add_client(mc_cilent,connfd);
client_ip = inet_ntoa(clientaddr.sin_addr);
}
for(int i =0; i < 20; i++)
{
char buf[100] = {0};
if(mc_cilent->client_fd[i] == -1)
{
break;
}
int n;
if(n = read((mc_cilent->client_fd[i]),buf,100) > 0)
{
printf("from %s : %s\n",client_ip,buf);
}
}
}
free(mc_cilent);
close(sock_fd);
close(connfd);
return 0;
}
1.2客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
if(argc != 3)
{
printf("传递ip和端口号\n");
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("open socket failed ");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(saddr);
int ret = connect(sockfd, (struct sockaddr *)&saddr, len);
if(ret == -1)
{
perror("connect failed : ");
return -1;
}
while(1)
{
char buf[100] = {0};
fgets(buf,100,stdin);
send(sockfd,buf,sizeof(buf),0);
}
return 0;
}
|