tcp网络编程4—并发的io多路复用实现(fcntl非阻塞)
fcnt_vector_fd.h #ifndef _FCNTL_VECTOR_FD_H #define _FCNTL_VECTOR_FD_H typedef struct{ int *fd; int conter; int max_conter; }VectorFd; extern VectorFd *create_vector_fd(void); extern void destroy_vector_fd(VectorFd *vfd); extern int get_fd(VectorFd *vfd, int index); extern void remove_fd(VectorFd *vfd, int fd); extern void add_fd(VectorFd *vfd, int fd); #endif
fcntl_vector_fd.c
#include <stdlib.h>
#include "fcntl_vector_fd.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
//void *memcpy(void *dest, const void *src, size_t n);
static void expansion_vector(VectorFd *vfd)
{
if(vfd->conter >= vfd->max_conter){
int *fds = (int *)malloc(vfd->conter + 5*sizeof(int));
if(fds == NULL){
perror("expansion:");
exit(1);
}
memcpy(fds, vfd->fd, sizeof(int) * vfd->conter);
free(vfd->fd);
vfd->fd = fds;
vfd->max_conter += 5;
}
return ;
}
static int indexof(VectorFd *vfd, int fd)
{
int index = 0;
for(;index < vfd->conter; index++){
if(vfd->fd[index] == fd){
return index;
}
}
return -1;
}
VectorFd *create_vector_fd(void)
{
VectorFd *vfd = (VectorFd *)malloc(sizeof(VectorFd));
if(vfd == NULL){
perror("create:");
exit(1);
}
vfd->fd = (int *)malloc(5*sizeof(int));
vfd->conter = 0;
vfd->max_conter = 5;
return vfd;
}
void destroy_vector_fd(VectorFd *vfd)
{
if(vfd == NULL){
perror("destroy:");
exit(1);
}
free(vfd->fd);
free(vfd);
}
int get_fd(VectorFd *vfd, int index)
{
if(vfd == NULL){
perror("get:");
exit(1);
}
if(index < 0 || index > vfd->conter -1){
return -1;
}
return vfd->fd[index];
}
void remove_fd(VectorFd *vfd, int fd)
{
if(vfd == NULL){
perror("remove:");
exit(1);
}
int index = indexof(vfd, fd);
if(index < 0){
return ;
}
for(;index < vfd->conter -1; index++){
vfd->fd[index] = vfd->fd[index +1];
}
vfd->fd[vfd->conter-1] = 0;
vfd->conter--;
}
void add_fd(VectorFd *vfd, int fd)
{
if(vfd == NULL){
perror("add:");
exit(1);
}
expansion_vector(vfd);//扩容
vfd->fd[vfd->conter] = fd;
vfd->conter++;
return ;
}
//fcntl_tcp_server.c //tcp_fcntl.c
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include "fcntl_vector_fd.h"
//int socket(int domain, int type, int protocol);
//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);
//void perror(const char *s);
VectorFd *vfd = NULL;
int socket_fd;
void sig_fun(int signo)
{
if(signo == SIGINT){
write(STDOUT_FILENO, "signal SIGINT", 15);
destroy_vector_fd(vfd);
close(socket_fd);
exit(1);
}
}
static void out_addr(struct sockaddr_in *caddr)
{
int port = ntohs(caddr->sin_port);
char ip[16];
memset(ip, 0, sizeof(ip));
if(inet_ntop(AF_INET, &caddr->sin_addr.s_addr, ip ,16) == NULL){
perror("out_addr:");
}
write(STDOUT_FILENO, ip ,sizeof(ip));
return ;
}
//如果客户端往socket中写了数据,子线程在遍历fd时,如果能读到数据,那就将数据作相应的处理,如果读不到将不会阻塞,直接返回
void do_service(int fd)
{
char buffer[512];
memset(buffer, 0, sizeof(buffer));
/*相当于不完整管道
*如果服务器从socket中读取到的数据为0,说明客户端关闭了socket或客户端已经挂掉了(服务器读一个写端关闭了的socket)
*如果服务器往读端(客户端)关闭了的socket中写数据,将会产生一个SIGPIPE的信号,并且errorno被设置为EPIPE(服务器写一个读端关闭了的socket)
*/
//采用非阻塞的read,若读不到数据就直接放回。直接服务于下一个客户端,因此不用判断size小于0的情况
ssize_t size = read(fd, buffer, sizeof(buffer));
if(size == 0){
char info[] = "client wtrite closed!";
write(STDOUT_FILENO, info, sizeof(info));
//write(STDOUT_FILENO, "client wtrite closed!", 25);
remove_fd(vfd, fd);
close(fd);
}
else if(size > 0){//为什么lent<0也能进入这个循环? 因为把read的返回值类型写成size_t了,实际上的返回值类型为ssize_t
write(STDOUT_FILENO, buffer, sizeof(buffer));
if(write(fd, buffer, size) <0 ){
if(errno == EPIPE){
perror("write");
remove_fd(vfd, fd);
close(fd);
}
perror("protacal error");
}
}
}
void *th_fun(void *arg)
{
int i ;
while(1){
i = 0;
for(i = 0; i < vfd->conter; i++){
do_service(get_fd(vfd, i));
}
}
return (void *)0;
}
int main(int argc,char *argv[])
{
if(argc <2){
perror("argc<2:");
exit(1);
}
if(signal(SIGINT,sig_fun) == SIG_ERR){
perror("signal:");
exit(1);
}
/*
*1. 创建socket
*/
if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket:");
exit(1);
}
/*
*2.绑定IP地址和端口号
*int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
*/
struct sockaddr_in serviceaddr;
memset(&serviceaddr, 0, sizeof(serviceaddr));
serviceaddr.sin_family = AF_INET;
serviceaddr.sin_port = htons(atoi(argv[1]));
serviceaddr.sin_addr.s_addr = INADDR_ANY;//一台服务器上可能有多块网卡(多个IP地址)
//这个宏是响应本机所有网卡(IP地址)上连接的客户端请求
if(bind(socket_fd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) < 0){
perror("bind:");
exit(1);
};
/*
*3.监听绑定的端口
*通知系统去监听来自客户端的连接请求
*(将监听到的客户端连接请求放置到对应的队列中)
*第二个参数:指定队列的长度
*/
if(listen(socket_fd, 10) < 0){
perror("listen:");
exit(1);
}
//创建动态数组
vfd = create_vector_fd();
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_t t;
/*
*5.创建子线程循环遍历动态数组
*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
* void *(*start_routine) (void *), void *arg);
*/
if(pthread_create(&t, &attr, th_fun, (void *)0) != 0){
perror("pthread_create");
exit(1);
}
pthread_attr_destroy(&attr);
/*
*4.调用accept函数从队列中获得一个客户端的连接请求
*并返回一个新的socket描述符,这个描述符和和客户端
*的连接请求对应,即这个描述符和某个客户端对应
*如果没有客户端连接,调用此函数会阻塞,直到获得一个客户端的连接
*第二个参数:客户端的地址信息
*/
struct sockaddr_in clientaddr;
socklen_t length = sizeof(clientaddr);
while(1){
int fd = accept(socket_fd, (struct sockaddr *)&clientaddr, &length);
if(fd < 0){
perror("accept:");
continue;
}
out_addr(&clientaddr);
//将套接字描述符设置为非阻塞的读写
int flags;
fcntl(fd, F_GETFL, &flags);
flags |= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) < 0){
perror("fcntl:");
exit(1);
}
add_fd(vfd, fd);
}
return 0;
}
//fcntl_tcp_client.c #include <stdio.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int main(int argc, char *argv[]) { int socket_fd; if(argc < 2){ printf("参数太少"); exit(1); } //1.创建socket if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ perror("socket:"); exit(1); } //2. 连接服务器 struct sockaddr_in serviceaddr; memset(&serviceaddr, 0, sizeof(serviceaddr)); serviceaddr.sin_family = AF_INET; serviceaddr.sin_port = htons(atoi(argv[2])); if(inet_pton(AF_INET, argv[1], &serviceaddr.sin_addr.s_addr) < 0){ perror("inet_pton:"); exit(1); } if(connect(socket_fd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) < 0){ perror("connect"); exit(1); } //3. read/write char buff[512]; ssize_t size; char *s = ">"; while(1){ write(STDOUT_FILENO, s, 1); memset(buff, 0, 1024); size = read(STDIN_FILENO, buff, sizeof(buff)); if(size <0 ) continue; buff[size -1] = '\0'; if(write(socket_fd, buff, sizeof(buff)) < 0){ perror("write"); continue; }else{ if((size1 = read(socket_fd, buff, sizeof(buff))) <0){ perror("read"); continue; }else{ printf("%s (%ld)\n",buff, size1); } } } //关闭套接字 close(socket_fd); return 0; }