select编程示例

代码中出现的errwrap.h和errwrap.c请参考我的上一篇文章

服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "errwrap.h"

#define MAXLINE 80
#define SERVERPORT 8000

int main(int argc, char *argv[])
{
		int i, maxi, maxfd, listenfd, confd, sockfd;
		int nret, client[FD_SETSIZE]; // FD_SETSIZE 默认为1024 
		ssize_t n;
		fd_set rset, allset;
		char buf[MAXLINE];
		char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 
		socklen_t clientaddr_len;
		struct sockaddr_in clientaddr, serveraddr;
		listenfd = Socket(AF_INET, SOCK_STREAM, 0);//获得一个监听fd
		
		bzero(&serveraddr, sizeof(serveraddr));
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
		serveraddr.sin_port = htons(SERVERPORT);
		Bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
		Listen(listenfd, 128); 

		maxfd = listenfd; //maxfd为现在监听的文件描述符的最大值
		maxi = -1; /* client[]的下标*/
		for (i = 0; i < FD_SETSIZE; i++)
				client[i] = -1; /* 用-1初始化client[] */

		FD_ZERO(&allset);
		FD_SET(listenfd, &allset); // 设置allset(所有监听文件描述符集合最新状态)
		for ( ; ; ) 
		{
				rset = allset; //当前文件描述符集合
				nret = select(maxfd+1, &rset, NULL, NULL, NULL);//检测“读”
				if (nret < 0)
						perr_exit("select error");
						if (FD_ISSET(listenfd, &rset)) //如果是新的连接到来
						{ 
								clientaddr_len = sizeof(clientaddr);
								confd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddr_len);
								printf("received from %s at PORT %d\n",
												inet_ntop(AF_INET, &clientaddr.sin_addr, str, sizeof(str)),
												ntohs(clientaddr.sin_port));
								for (i = 0; i < FD_SETSIZE; i++)
								{
										if (client[i] < 0) 
										{
												client[i] = confd; //将新的文件描述符添加到client里
												break;
										}
								}
								// 达到select能监控的文件个数上限1024 
								if (i == FD_SETSIZE) 
								{
										fputs("too many clients\n", stderr);
										exit(1);
								}
								FD_SET(confd, &allset); //将新的新的文件描述符添加到监控信号集里
								if (confd > maxfd)
										maxfd = confd; //更新最大的文件描述符
								if (i > maxi)
										maxi = i; // 更新client最大下标
								if (--nret == 0)
										continue; /* 如果没有更多的就绪文件描述符继续回到上面select阻塞监听,负责处理未
													 处理完的就绪文件描述符*/
						}
				for (i = 0; i <= maxi; i++) 
				{ // 检测哪个clients 有数据就绪
						if ( (sockfd = client[i]) < 0)
								continue;
						if (FD_ISSET(sockfd, &rset)) //对连接到服务器的每个客户机(文件描述符),检测是否有数据到达
						{
								if ( (n = Read(sockfd, buf, MAXLINE)) == 0) 
								{
										// 当client关闭链接时,服务器端也关闭对应链接
										Close(sockfd);
										FD_CLR(sockfd, &allset); // 解除select监控此文件描述符
										client[i] = -1;
								} else 
								{//处理每个有数据到达的fd
										int j;
										for (j = 0; j < n; j++)
												buf[j] = toupper(buf[j]);
												Write(sockfd, buf, n);
								}
								if (--nret == 0)//处理的那个fd不是最后一个
										break;
						}
				}
		}
		close(listenfd);
		return 0;
}

客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "errwrap.h"

#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char *argv[])
{
		if(argc<2)
		{
			err_exit("参数过少");
		}
		struct sockaddr_in servaddr;
		char buf[MAXLINE];
		int sockfd, n;
		sockfd = Socket(AF_INET, SOCK_STREAM, 0);
		bzero(&servaddr, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
		servaddr.sin_port = htons(SERV_PORT);
		Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
		while (fgets(buf, MAXLINE, stdin) != NULL) 
		{
				Write(sockfd, buf, strlen(buf));
				n = Read(sockfd, buf, MAXLINE);
				if (n == 0)
						printf("the other side has been closed.\n");
				else
						Write(STDOUT_FILENO, buf, n);
		}
		Close(sockfd);
		return 0;
}



GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐