Linux下select/子进程/poll实现socket并发服务

/*commom.h*/
#ifndef _COMMOM_H_
#define _COMMOM_H_

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SIZE 512

char buf[SIZE];

int create_socket()
{
	int lisfd;
	lisfd = socket(AF_INET, SOCK_STREAM, 0);//IPV4 数据流 TCP类型
	if (lisfd == -1)
	{
		perror("create_socket failed!\n");
		exit(1);
	}
	return lisfd;
}

int sock_bind(int lisfd, int port)
{
	struct sockaddr_in myaddr;
	memset((char *)&myaddr, 0, sizeof(struct sockaddr_in));//清零
	myaddr.sin_family = AF_INET;//IPV4
	myaddr.sin_port = htons(port);//端口
	myaddr.sin_addr.s_addr = htonl(INADDR_ANY);//允许连接到所有本地地址上
	if (bind(lisfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr))==-1)
	{
		perror("sock_bind failed!\n");
		exit(1);
	}
	return 0;
}

int sock_listen(int lisfd, int max_con)
{
	if (listen(lisfd, max_con)==-1)
	{
		perror("sock_listen failed!\n");
		exit(1);
	}
	return 0;
}

int sock_accept(int lisfd)
{
	struct sockaddr_in remoaddr;
	int clifd;
	socklen_t size = sizeof(struct sockaddr);
	
	memset((char *)&remoaddr, 0, sizeof(struct sockaddr));
	clifd = accept(lisfd, (struct sockaddr *)&remoaddr, &size);
	if (clifd == -1)
	{
		perror("sock_accept failed!\n");
		exit(1);
	}
	printf("clifd: %d\n", clifd);
	printf("connected from %s, port: %d\n", inet_ntoa(remoaddr.sin_addr), ntohs(remoaddr.sin_port));
	return clifd;
}

int sock_connect(int clifd, char *ipaddr, int port)
{
	struct sockaddr_in remoaddr;
	memset((char *)&remoaddr, 0, sizeof(struct sockaddr_in));
	remoaddr.sin_family = AF_INET;//IPV4地址
	remoaddr.sin_port = htons(port);//端口
	remoaddr.sin_addr.s_addr = inet_addr(ipaddr);//服务器端IP地址
	if (connect(clifd, (struct sockaddr *)&remoaddr, sizeof(struct sockaddr))==-1)
	{
		perror("sock_connect failed!\n");
		exit(1);
	}
	return 0;
}

int sock_send(int sockfd, void *buf, size_t len, int flags)
{
	int nbytes;
	nbytes = send(sockfd, buf, len, flags);
	if (nbytes == -1)
	{
		perror("sock_send failed!\n");
		exit(1);
	}
	return nbytes;
}

int sock_recv(int sockfd, void *buf, size_t len, int flags)
{
	int nbytes;
	nbytes = recv(sockfd, buf, len, flags);
	if (nbytes == -1)
	{
		perror("sock_send failed!\n");
		exit(1);
	}
	return nbytes;
}
#endif
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include "commom.h"
#include <errno.h>
#include <sys/wait.h>

#define MAXNUM 10
#define SELECT      //定义选用SELECT并发服务,否则用fork()子进程并发服务


void sig_child(int signo)/*用于fork并发时候使用,防止出现僵死进程*/
{
	int stat;
	pid_t pid;
	while((pid=waitpid(-1, &stat, WNOHANG)) > 0)
	{
		printf("child %d terminated.\n", pid);
	}
	return ;
}

int main(int argc, char **argv)
{
	if (argc != 2)
	{
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}
	int lisfd, clifd, sockfd;
	int i, n, ret;
	int nready;
	int client[FD_SETSIZE];
	int maxfd, maxi;
	fd_set rset;
	fd_set allset;

	signal(SIGCHLD, sig_child);
	signal(SIGPIPE, SIG_IGN);//忽略对端TCP终止的信号
	
	bzero(buf, sizeof(buf));
	lisfd = create_socket();
	int opt = 1;
	setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//地址重用
	sock_bind(lisfd, atoi(argv[1]));
	sock_listen(lisfd, MAXNUM);
	
	maxfd = lisfd;
	maxi = -1;
	for (i=0; i<FD_SETSIZE; i++)//初始化客户端数组,为-1表示可用
	{
		client[i] = -1;
	}
	FD_ZERO(&allset);
	FD_SET(lisfd, &allset);
#ifdef SELECT	
	while(1)
	{
		rset = allset;
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		if (FD_ISSET(lisfd, &rset))//新的客户端连接
		{
			clifd = sock_accept(lisfd);
			for (i=0; i<FD_SETSIZE; i++)
			{
				if (client[i] < 0)
				{
					client[i] = clifd;//保存文件描述符
					break;
				}	
			}
			if (i == FD_SETSIZE)
			{
				printf("too many client!\n");
				exit(1);
			}
			FD_SET(clifd, &allset);//增加一个描述符到集合里
			if (clifd > maxfd)
			{
				maxfd = clifd;
			}
			if (i > maxi)
			{
				maxi = i;
			}
			if (--nready <= 0)
			{
				continue;//没有可读的数据
			}
		}
		for (i=0; i<= maxi; i++)//检测所有客户端的数据
		{
			if ((sockfd = client[i])<0)
			{
				continue;
			}
			if (FD_ISSET(sockfd, &rset))
			{
				n = sock_recv(sockfd, buf, sizeof(buf), 0);
				if (n == 0)
				{
					printf("The other side has been closed.\n");
					fflush(stdout);
					close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				}
				else
				{
					printf("recv: %s\n", buf);
					for (i=0; i<strlen(buf); i++)
					{
						buf[i] = toupper(buf[i]);
					}
					sock_send(sockfd, buf, 512, 0);
				}
				if (--nready <= 0)
				{
					break;
				}
			}
		}
	}
#else
	while(1)
	{
		clifd = sock_accept(lisfd);//套接字引用计数为1
		if (errno == EINTR)
			continue;//中断时继续
		ret = fork();//fork之后套接字共享,引用计数加一(为2)
		if (ret == -1)
		{
			perror("fork failed!\n");
			exit(1);
		}else if (ret == 0)//子进程
		{
			close(lisfd);//套接字引用计数减一
			while(1)
			{
				sock_recv(clifd, buf, sizeof(buf), 0);
				printf("recv: %s\n", buf);
				for (i=0; i<strlen(buf); i++)
				{
					(buf)[i] = toupper((buf)[i]);
				}
				sock_send(clifd, buf, 512, 0);
			}
			close(clifd);//关闭连接,引用计数减一,为0时释放资源
			exit(1);
		}else
		{
			close(clifd);//套接字引用计数减一
		}
	}
#endif	
	return 0;
}




 
/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "commom.h"

int main(int argc, char **argv)
{
	if (argc != 3)
	{
		printf("Usage: %s <server_ipaddr> <port>\n", argv[0]);
		exit(1);
	}
	int clifd;
	
	signal(SIGCHLD, SIG_IGN);//忽略信号
	signal(SIGPIPE, SIG_IGN);
	
	bzero(buf, sizeof(buf));
	clifd = create_socket();
	sock_connect(clifd, argv[1], atoi(argv[2]));
	while(fgets(buf, 512, stdin) != NULL)
	{
		sock_send(clifd, buf, 512, 0);
		sock_recv(clifd, buf, sizeof(buf), 0);
		printf("recv: %s\n", buf);
	}
	close(clifd);
	return 0;
}

用poll来实现的server.c

/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include "commom.h"
#include <sys/poll.h>
#include <errno.h>

#define MAXNUM 10
#define MAXCON 10

#define INFTIM -1 //

int main(int argc, char **argv)
{
	if (argc != 2)
	{
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}
	int lisfd, clifd, sockfd;
	int i, n;
	int nready;
	int maxi;

	struct pollfd client[MAXCON];
	signal(SIGPIPE, SIG_IGN);//忽略对端TCP终止的信号
	
	bzero(buf, sizeof(buf));
	lisfd = create_socket();
	int opt = 1;
	setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//地址重用
	sock_bind(lisfd, atoi(argv[1]));
	sock_listen(lisfd, MAXNUM);
	
	maxi = 0;
	client[0].fd = lisfd;   //client[0]用作监听
	client[0].events = POLLIN;
	
	for (i=1; i<MAXCON; i++)//初始化客户端数组,为-1表示可用
	{
		client[i].fd = -1;
	}

	while(1)
	{
		nready = poll(client, maxi+1, INFTIM);
		if (client[0].revents & POLLIN)//新的客户端连接
		{
			clifd = sock_accept(lisfd);
			for (i=1; i<MAXCON; i++)
			{
				if (client[i].fd < 0)
				{
					client[i].fd = clifd;//保存文件描述符
					client[i].events = POLLIN;//测试条件普通数据可读
					break;
				}	
			}
			if (i == MAXCON)
			{
				printf("too many client!\n");
				exit(1);
			}
			if (i > maxi)
			{
				maxi = i;
			}
			if (--nready <= 0)
			{
				continue;//没有可读的数据
			}
		}
		for (i=1; i<= maxi; i++)//检测所有客户端的数据
		{
			if ((sockfd = client[i].fd)<0)
			{
				continue;
			}
			if (client[i].revents & (POLLIN|POLLERR))
			{
				n = sock_recv(sockfd, buf, sizeof(buf), 0);
				if (n == 0 || errno == ECONNRESET)
				{
					printf("The other side has been closed.\n");
					fflush(stdout);
					close(sockfd);
					client[i].fd = -1;
				}
				else
				{
					printf("recv: %s\n", buf);
					for (i=0; i<strlen(buf); i++)
					{
						buf[i] = toupper(buf[i]);
					}
					sock_send(sockfd, buf, 512, 0);
				}
				if (--nready <= 0)
				{
					break;
				}
			}
		}
	}
	return 0;
}




#makefile
CFLAGS = -Wall -g
CC = gcc
TARGET1 = server
TARGET2 = client

all:$(TARGET1) $(TARGET2)
$(TARGET1):$(TARGET1).o
		$(CC) $(CFLAGS) -o $@ $^
$(TARGET2):$(TARGET2).o
		$(CC) $(CFLAGS) -o $@ $^
%.o:%.c %.h
		$(CC) -c $(CFLAGS) -o $@ $<
clean:
		rm -f $(TARGET1) $(TARGET2) *.o



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值