全部博文(1159)
分类: 嵌入式
2011-06-17 15:14:25
09嵌入式方向 计应(2)班 刘峰
select注释执行过程
/* server.c */
#include
#include
#include
#include
#include "wrap.h" /* wrap.c头文件*/
#define MAXLINE 80 /* 宏定义通道 MAXLINE值为80 */
#define SERV_PORT 8000 /* 宏定义端口为8000 */
int main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];//nready 存放有数据请求的个数,client[FD_SETSIZE]存放数据请求客户端的最大连接数为FD_SETSIZE(1024)
ssize_t n; //定义一个有符号size_t变量n
fd_set rset, allset;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
socklen_t cliaddr_len;
struct sockaddr_in cliaddr, servaddr;//定义两个结构体
listenfd = Socket(AF_INET, SOCK_STREAM, 0);//打开一个网络通信端口地址类型IPV4,通信类型TCP,并且将值赋给文件描述符变量listenfd
bzero(&servaddr, sizeof(servaddr));//将服务器结构体清零
servaddr.sin_family = AF_INET;//服务器地址类型ipv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//ip地址为本地的任意IP地址,可以在所有IP地址上监听
servaddr.sin_port = htons(SERV_PORT);//端口号为SERV_PORT,定义为8000
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//服务端调用bind邦定端口号和服务器地址
Listen(listenfd, 20);//监听网路通讯端口最到允许20个客户端接入
maxfd = listenfd; /* 将tcp连接端口初始化赋值给maxfd */
maxi = -1; /* 初始化客户端 */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* 初始化连接空间 */
FD_ZERO(&allset);//将&allset端口集清零
FD_SET(listenfd, &allset);//将listenfd文件描述符添加到&allset描述符集中
for ( ; ; ) {
rset = allset; /* 把存在allset中的文件描述符中的值赋rset */
nready = select(maxfd+1, &rset, NULL, NULL, NULL);//保存连接描述符到nready中
if (nready < 0)//判断select是否成功失败返回"select error"
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* 创建新的客户端连接将值放入 &rset中*/
cliaddr_len = sizeof(cliaddr);//得到客户端缓冲区长度
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);//服务器接受客户端连接请求传出客户端地址传入缓冲区长度
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));//输出连接客户端地址和端口号
for (i = 0; i < FD_SETSIZE; i++) //循环i的值要小于客户端的最大连接数
if (client[i] < 0) {
client[i] = connfd; /* 如果请求连接个数小于零将已有的连接客户端文件描述保存在client数组中 */
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients\n", stderr);
exit(1);//如果i的值已经达到连接的最大上限输出错误“超过连接客户端的总数”
}
FD_SET(connfd, &allset); /* 将新文件描述符添加到&allset中 */
if (connfd > maxfd)
maxfd = connfd; /* 若连接的客户端大于请求客户端将连接客户端赋值给 maxfd*/
if (i > maxi)
maxi = i; /* 若已有的连接客户端总个数大于已连接的客户端数,则会为已连接的客户端创建索引 */
if (--nready == 0)
continue; /* 没有客户端数据请求进行下一轮 select*/
}
for (i = 0; i <= maxi; i++) { /* 检查所有请求端口的个数 */
if ( (sockfd = client[i]) < 0)//把 client[i]文件描述符赋值给sockfd如果小于0则没有请求客户端
continue;
if (FD_ISSET(sockfd, &rset)) {//判断sockfd文件描述符是否在&rset文件描述符集中
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {//读出客户端数据是否为0
/* connection closed by client */
Close(sockfd);//关闭通信端口
FD_CLR(sockfd, &allset);//从&allset中清除文件描述符数据
client[i] = -1; //n不为零表示有客户端数据请求
} else {
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);//把buf客户端的数据请求转化为大写
Write(sockfd, buf, n);//将转化后数据发给客户端
}
if (--nready == 0)//无数据写入请求则结束循环
break; /* no more readable descriptors */
}
}
}
}
总结:此程序是用select实现服务器与多个客户端进行数据交互的过程。
1)嵌套字初始 化:调用函数Socket(),成功返回一套接字文件描述符赋值给listenfd.然后将结构体servaddr清空嵌套字初始化,连接服务器,打开一 个网络通信端口,创建服务器结构体,绑定端口号和服务地址,将客户端套接字描述符赋值到allset rest集中,调用listen函数声明服务器处于监听状态最大允许接入客户端为20个将监听到的文件描述符listenfd赋给maxfd,给maxi 赋初值为-1,使用for循环对数组client[]初始化
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1;
将allset清空,将listenfd文件描述符添加到&allset描述符集中
2)服务器与客户端进行三次握手:
将allset 集赋值给rest 调用select函数将客户端与服务器的连接数保存在nready中,如果nready小于0则返回出错,nready大于零件描述符listenfd在 集合rset中,就执行Accept()函数,返回一个新的文件描述符赋给connfd.打印出客户端的ip地址与端口号.
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if (nready < 0)
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
cliaddr_len = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
将新得到的文件描述符connfd保存到数组client[]中.把新得到的文件描述connfd符写入allset集合中
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
判断客户端的连接数是否达到最大的上限值,如果达到就输出错误。
if (i == FD_SETSIZE) {
fputs("too many clients\n", stderr);
exit(1);
}
将新文件描述符 添加到allset集中,通过if语句把allset中的文件描述符和i的最大值找出来分别赋给maxfd,maxi.把nready的值自减1,当 nready为0,没有更多的文件描述符,结束本次循环.此时就只有一个文件描述符listenfd,所以nready的值是1,自减后不等于0就不执行 con进入for语句把connfd赋给sockfd因为等于0,就结束本次循环.进入下次for循环,重新监听来自客户端的连接请求.
for循环检查 所有客户端数,if ( (sockfd = client[i]) < 0))没有客户端数据请求,if语句判断sockfd文件描述符是否在rset文件集中。如果读出的客户端数据为0关闭连接从allset文件描述符中清 除文件描述,除此把客户端传入的数据进行处理,处理完后返回客户端。
客户端1
[root@bogon Desktop]# gcc client.c -o client
[root@bogon Desktop]# ./client
ff
FF
fdsadfdsfdsf
FDSADFDSFDSF
客户端2
[root@bogon ~]# cd /root/Desktop/
[root@bogon Desktop]# gcc client.c -o client
[root@bogon Desktop]# ./client
dshfasdjf
DSHFASDJF
fsdafdsf
FSDAFDSF
客户端3
[root@bogon ~]# cd /root/Desktop/
[root@bogon Desktop]# gcc client.c -o client
[root@bogon Desktop]# ./client
kkkdslfdlfs
KKKDSLFDLFS
ff
FF
服务器端
[root@bogon Desktop]# gcc server.c -o server
[root@bogon Desktop]# ./server
received from 127.0.0.1 at PORT 58471
received from 127.0.0.1 at PORT 58472
received from 127.0.0.1 at PORT 58473