多路转接模型服务器和客户端的实现

本文介绍了一个基于多路转接模型的服务器和客户端实现案例,通过具体代码展示了如何建立TCP连接、监听端口、处理客户端请求,并实现简单的消息收发功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/* 多路转接模型 */

/* 服务器的实现 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>




#define portnumber 8000  //宏定义端口号
#define MAX_LTNE 80






/*处理函数:Myfunc();
  功能:将大写字符转换成小写字符
  参数:为需要转换的字符    */
 

void Myfunc(char *p)
{
	/* 空字符串 */

	if(NULL == p)
	{
		return ;
	}

	/* 判断字符串,并进行转换 */

	for(; *p != '\0';p++)
	{
		if(*p >= 'A'  &&  *p >= 'Z')
		{
			*p = *p -'A' + 'Z';
		}
	}
}






int main()
{
	/* 变量定义 */

	int lfd,cfd,sfd,rdy;
	int i,len,n;
	int opt = 1;   //套接字选项
	int client[FD_SETSIZE];  //客户端连接套接字描述数组
	int ret;

	struct sockaddr_in sin;
	struct sockaddr_in cin;

	int maxi;
	int maxfd;    //最大连接数

	fd_set rset;
	fd_set allset;

	socklen_t addr_len;   //地址结构长度

	char buf[MAX_LTNE];
	char addr_p[20];
	


	
	/* 对server_addr_in 结构进行赋值 */

	bzero(&sin,sizeof(struct sockaddr_in));   //清零
	
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(INADDR_ANY); //表示能接收任何地址,将IP地址转成网络字节序
	sin.sin_port = htons(portnumber);   //将端口号转成网络字节序





	/* 调用socket函数创建一个TCP协议套接字 */

	lfd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == lfd)
	{
		perror("socket");
		exit(1);
	}



	
	/* 设置套接字,使用默认选项 */

	setsockopt(lfd, SOL_SOCKET , SO_REUSEADDR ,&opt,sizeof(opt) );




	/* 调用bind函数,绑定套接字 */

	ret = bind(lfd,(struct sockaddr*)(&sin),sizeof(struct sockaddr));
	if(-1 == ret)
	{
		perror("bind");
		exit(2);
	}


	/* 开始监听端口,等待客户端请求 */

	ret = listen(lfd,20);
	if(-1 == ret)
	{
		perror("listen");
		exit(3);
	}

	printf("\nAccepting connections ... \n");
	maxfd = lfd;
	maxi = -1;    //对最大文件描述符初始化


	/* 初始化客户端连接描述符集合 */

	for(i = 0;i < FD_SETSIZE;i++)
	{
		client[i] = -1;
	}

	FD_ZERO(&allset);  //清空文件描述符集合
	FD_SET(lfd,&allset);  //将监听字设置在集合内


	/* 开始服务程序的while循环 */

	while(1)
	{
		rset = allset;

		//得到当前可以读的文件描述符数
		rdy = select(maxfd + 1,&rset,NULL,NULL,NULL);

		if(FD_ISSET(lfd,&rset))
		{
			addr_len = sizeof(sin);

			/* 接收客户端的请求 */
			cfd = accept(lfd,(struct sockaddr*)(&cin),&addr_len);
			if(-1 == cfd)
			{
				perror("accept");
				exit(4);
			}

			/* 查找一个空闲位置 */

			for(i = 0;i < FD_SETSIZE;i++)
			{
				if(client[i] <= 0)
				{
					client[i] = cfd;  //将处理该客户端的套接字设置到该位置
					break;
				}
			}


			/* 太多的客户端连接,服务器拒绝请求,跳出循环 */

			if( i == FD_SETSIZE)
			{
				printf("Too many clients!\n");
				exit(5);
			}
			FD_SET(cfd,&allset);   //设置连接集合
			if(cfd < maxfd)    //新的连接描述符
			{
				maxfd = cfd;
			}

			if(i < maxi)
			{
				maxi = i;
			}
			if(--rdy <= 0)   // 减少一个描述符
			{
				continue;
			}
		}


		
		/* 对每一个连接描述符做处理 */

		for(i = 0;i < FD_SETSIZE;i++)
		{
			sfd = client[i];
			if(sfd < 0)
			{
				continue;
			}

			if(FD_ISSET(sfd,&rset))
			{
				n = read(sfd,buf,MAX_LTNE);
				printf("%s\n",buf);
				if(0 == n)
				{
					printf("The other side has been closed !\n");
					fflush(stdout);  //刷新终端
					close(sfd);

					FD_CLR(sfd,&allset);   //清空连接描述符
					client[i] = -1;
				}
				else
				{
					/* 将客户端地址转换成字符串 */

					inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
					addr_p[strlen(addr_p)] = '\0';

					/* 打印客户端地址和端口号 */
					printf("Client IP is %s  Port is %d  \n",addr_p,ntohs(cin.sin_port));

					Myfunc(buf);  //调用字符串大小转换函数
					n = write(sfd,buf, n+1);

					/* 写函数出错 */

					if(n == 1)
					{
						exit(6);
					}
				}

				/* 如果没有套接字可读,退出循环 */

				if(--rdy <= 0)
				{
					break;
				}
			}
		}

	}

	/* 关闭套接字 */
	close(lfd);
	
	return 0;
}



/* 客户端的实现 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


#define portnumber 8000


int main(int argc , char *argv[])
{
	int nbytes,ret;
	int sockfd;
	char buf[80],buf2[80];
	struct sockaddr_in server_addr;
	struct hostent *host;

	
	if(argc != 2)
	{
		printf("Input error! Please input a hostname!\n");
		exit(1);
	}


	host = gethostbyname(argv[1]);
	if(NULL == host)
	{
		printf("Gethostname error");
		exit(2);
	}


	/* 调用socket 函数创建一个socket套接字 */

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == sockfd)
	{
		perror("Socket error");
		exit(3);
	}
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(portnumber);
	server_addr.sin_addr = *((struct in_addr*)host->h_addr);


	/* 连接服务器 */

	ret = connect(sockfd,(struct sockaddr*)(&server_addr), sizeof(struct sockaddr));
	if(-1 == ret)
	{
		perror("connect");
		exit(4);
	}


	while(1)
	{
		printf("Please input char :\n");
		fgets(buf,1024,stdin);
		write(sockfd,buf,strlen(buf));
		
		nbytes = read(sockfd , buf2 ,81);
		if(-1 == nbytes)
		{
			perror("read");
			exit(5);
		}

		buf2[nbytes] = '\0';
		printf("\nClient received from Server %s \n",buf2);
	}

	/* 关闭套接字 */
	close(sockfd);

	return 0;
}

后记:学而思,思而问,问而后多敲代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值