全部博文(1159)
分类: 嵌入式
2011-06-17 08:25:57
院系:计算机与信息工程学院 班级: 09计算机应用二班
姓名:赵晓晓(09嵌入式方向) 学号:0906042051
2009嵌入式 server-client 服务器-客户端
/* server.c */
#include
#include
#include
#include
#include "wrap.h"
#define MAXLINE 80 /*通道最大为80*/
#define SERV_PORT 8000 /*宏定义端口号为8000*/
int main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_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); /*成功返回一套接字文件描述符赋给listenfd,,如果socket()调用出错则返回-1*/
/*下面四行是对Myaddr参数的初始化:*/
bzero(&servaddr, sizeof(servaddr)); /*将结构体servaddr清零*/
servaddr.sin_family = AF_INET; /*设置地址类型为AF_INET,对于Ipv4,family参数指定AF_INET
*/
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*网络地址为INADDR_ANY,INADDR_ANY表示本地的任意IP地址*/
servaddr.sin_port = htons(SERV_PORT); /*端口号为SERV_PORT,宏定义为8000.*/
Bind(listenfd,
(struct sockaddr *)&servaddr, sizeof(servaddr));
/*bind将本地网络地址和端口号与服务器网络地址和端口号绑定在一起,实现本地与服务器的通信.bind()成功返回0,失败返回-1*/
Listen(listenfd, 20); /*监听listenfd的状态,并且最多允许监听20个,如果接收到更多的连接请求就忽略.*/
maxfd = listenfd; /* 给maxfd初始化 */
maxi = -1; /* 给maxi初始化 */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* 给client[]初始化为-1 ,库函数定义的为1024,把client[]的元素都初始化为-1
*/
FD_ZERO(&allset); /*cccallset集合,用于存放socket文件描述符*/
FD_SET(listenfd, &allset); /*把监听到的文件描述listenfd符写入allset集合中*/
for ( ; ; ) {
rset = allset; /*将已有的连接赋值给rset */
nready = select(maxfd+1, &rset, NULL, NULL, NULL); /*可以监听多个文件描述符,当有数据到达时就处理,并返回更新的个数给nready,没有数据到达就阻塞*/
if (nready < 0) /*当nready小于0时出错*/
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /*测试文件描述符listenfd是否在rset集中 */
cliaddr_len = sizeof(cliaddr); /*得到客户端地址长度*/
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr,
&cliaddr_len);
/*服务器调用accept()时没有客户端的连接请求,就阻塞等待直到有客户端连接上来,如有客户端连接则解除阻塞状态,并返回一个服务器与客户端互相
通信的文件描述符connfd*/
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), /*将cliaddr.sin_addr地址装换成字符串, 并返回字符首地址*/
ntohs(cliaddr.sin_port)); /*网络字节序转换为主机字节序/**/
for (i = 0; i < FD_SETSIZE; i++) /*查找最小没用的数组元素,并将文件描述符connfd保存起来*/
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */ /*将已连接的客户端的文件描述符保存在client数组里*/
break;
}
if (i == FD_SETSIZE) { /*如果最小没用的数组元素与数组最大容量相等时出错,有太多的客户端*/
fputs("too many clients\n", stderr);
exit(1);
}
FD_SET(connfd, &allset); /* 把新得到的文件描述connfd符写入allset集合中 */
if (connfd > maxfd) /*设置最大文件描述符*/
maxfd = connfd; /* for select */ /*将最新的描述符保存在maxfd*/
if (i > maxi) /*设置i的最大值*/
maxi = i; /* max index in client[] array */ /*将已有的连接总数保存在maxi里*/
if (--nready == 0) /*当nready为0,没有更多的文件描述符,结束本次循环*/
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0) /*如果没有连接则结束此次循环*/
continue;
if (FD_ISSET(sockfd, &rset)) { /*如果sockfd在rset里则执行下面的语句*/
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { /*判断客户端是否关闭连接*/
/* connection closed by client */
Close(sockfd); /*如果客户端关闭了,服务器端也关闭*/
FD_CLR(sockfd, &allset); /*从allset清除sockfd的文件描述符*/
client[i] = -1; /*将这个空间置为没有连接*/
} else { /*如果客户端没有关闭,服务器接受来自客户端的数据,并处理,完成后并返回给客户端*/
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]); /*将小写转换成大写*/
Write(sockfd, buf, n); /*将处理后的数据从buf中写回客户端*/
}
if (--nready == 0) /*当nready=0时,数据处理完,结束本次循环*/
break; /* no more readable descriptors */
}
}
}
}
/*执行过程:
调用函数Socket(),成功返回一套接字文件描述符赋值给listenfd.
然后将结构体servaddr清空,设置ipv4地址类型为AF_INET,NADDR_ANY为本地的任意IP地址,端口号宏定义为8000.
调用函数Bind(),将本地的套接字listenfd与服务器网络地址和端口号绑定在一起
在服务器端调用Listen()数来监听客户端状态.将监听到的文件描述符listenfd赋给maxfd,给maxi赋初值为-1,使用for循环对数组client[]初始化,将allset集合清空,把监听到的文件描述listenfd符写入allset集合中
进入for循环,把allset里的文件描述符赋给rset(把listenfd的文件描述符赋给rset).
调用select函数,当客户端有连接请求时,将客户端请求个数返回给nready,如果有一个请求,则nready = 1.
if判断nready不小于0,不出错.此时文件描述符listenfd在集合rset中,就执行Accept()函数,返回一个新的文件描述符赋给
connfd.打印出客户端的ip地址与端口号.将新得到的文件描述符connfd保存到数组client[]中.把新得到的文件描述connfd符写入
allset集合中,通过if语句把allset中的文件描述符和i的最大值找出来分别赋给maxfd,maxi.把ready的值自减1,当
nready为0,没有更多的文件描述符,结束本次循环.此时就只有一个文件描述符listenfd,所以nready的值是1,自减后不等于0就不执行
con进入for语句把connfd赋给sockfd因为等于0,就结束本次循环.进入下次for循环,重新监听来自客户端的连接请求.
在本次循环中在rset,allset中有俩个文件描述符分别是listenfd,connfd,如果listenfd没有数据到达,connfd有数据 到达,则由select得到nready值为1,if判断rset中的第一个文件描述符listenfd没有连接请求,退出if语句,进入下一个for循 环.将connfd赋给sockfd.因为connfd有连接请求,并判断客户端没有关闭连接,接收来自客户端的数据并将小写装换为大写重新返回给客户 端;如果客户端关闭连接,服务器端也关闭连接,并从allset清除sockfd的文件描述符.把ready的值自减1,当nready为0,没有更多的 文件描述符,结束本次循环.此时就只有一个文件描述符connfd,所以nready的值是1,自减后等于0,就结束本次循环.进入下次for循环,重新 监听来自客户端的连接请求.
在本次循环中在rset,allset中有俩个文件描述符分别是listenfd,connfd,如果linstenfd和connfd都有连接请求,则 select返回值是2,nready旳值也是2.if判断nready不小于0,不出错.此时文件描述符listenfd在集合rset中,就执行 Accept()函数,返回一个新的文件描述符赋给connfd.打印出客户端的ip地址与端口号.将新得到的文件描述符connfd1保存到数组 client[]中.把新得到的文件描述connfd1符写入allset集合中,此时allset集合中有三个文件描述符分别是listenfd、 connfd、connfd1.通过if语句把allset中的文件描述符和i的最大值找出来分别赋给maxfd,maxi.把ready的值自减 1,nraedy的值是1,1不等于0,就不执行continue,进入for语句把connfd赋给sockfd,因为connfd有连接请求并判断客 户端没有关闭连接,接收来自客户端的数据并将小写装换为大写重新返回给客户端;如果客户端关闭连接,服务器端也关闭连接,并从allset清除 sockfd的文件描述符.把ready的值自减1,nready为0,没有更多的文件描述符,结束本次循环.进入下次for循环,重新监听来自客户端的 连接请求.
实验执行结果:
服务器端:
[root@localhost Desktop]# gcc server.c -o server
[root@localhost Desktop]# ./server
received from 127.0.0.1 at PORT 57729
received from 127.0.0.1 at PORT 57732
客户端1:
[root@localhost Desktop]# gcc client.c -o client
[root@localhost Desktop]# ./client
wowowo
WOWOWO
lsllallal
LSLLALLAL
zhaoxiaoxiao
ZHAOXIAOXIAO
xxxxxx
XXXXXX
ooooooo
OOOOOOO
客户端2:
[root@localhost Desktop]# gcc client.c -o client
[root@localhost Desktop]# ./client
nihsoa
NIHSOA
womendouhenhao
WOMENDOUHENHAO
xiexie
XIEXIE
nihao
NIHAO
zhaoxiaoxiao
ZHAOXIAOXIAO
xiaoxiao
XIAOXIAO
xzhao
XZHAO
*/