#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h> #define MAXBUF 1024 /************关于本文档******************************************** *filename: async-server.c *purpose: 演示网络异步通讯,这是服务器端程序 *wrote by: zhoulifa(zhoulifa@163 Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言 *date time:2007-01-25 21:22 *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途 * 但请遵循GPL *Thanks to: Google.com *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力 * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献! *修改、添加注释 by: nickolas *********************************************************************/ int main(int argc, char **argv) { int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; //服务器绑定地址,客户端协议地址 unsigned int myport, lisnum; char buf[MAXBUF + 1]; char usrname[MAXBUF + 1], hostname[MAXBUF + 1]; int flag = 1 ; fd_set rfds; struct timeval tv; int retval, maxfd = -1; printf("请输入您的用户名:"); bzero(hostname, MAXBUF + 1); fgets(hostname, MAXBUF, stdin); if (argv[1]) myport = atoi(argv[1]); else myport = 7838; if (argv[2]) lisnum = atoi(argv[2]); else lisnum = 2; //创建套接字,指定通信类型,返回套接字描述字 if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { //协议簇:ipv4,套接字类型:字节流(使用TCP),协议类型:缺省0 perror("socket"); exit(1); } /* 使用sockaddr_in这个结构来设置/获取地址信息。 struct sockaddr_in { short sin_family; u_short sin_port struct in_addr sin_addr; char sin_zero[8]; }; sin_family 指代协议族,在socket编程中只能是AF_INET sin_port 存储端口号(使用网络字节顺序) sin_addr 存储IP地址,使用in_addr这个数据结构 struct in_addr { unsigned long s_addr; }; 这个数据结构是由于历史原因保留下来的,主要用作与以前的格式兼容。 s_addr 按照网络字节顺序存储IP地址 sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。 */ bzero(&my_addr, sizeof(my_addr)); //extern void bzero(void *s, int n);置字节字符串s的前n个字节为零。 my_addr.sin_family = PF_INET; //协议簇 my_addr.sin_port = htons(myport); //将一个16位数(端口号)从主机字节顺序转换成网络字节顺序。 if (argv[3]) //按照网络字节顺序存储IP地址 my_addr.sin_addr.s_addr = inet_addr(argv[3]); else my_addr.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY 通配符地址告诉系统假如有多个接口,本服务器进程将接受任意一个Internet 接口的连接。 //int bind( int sockfd, const struct sockaddr *myaddr, socket_t addrlen );给socket分配本地协议地址(ipaddr和port). if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } //int listen(int sockfd, int backlog);表明服务器进程愿意接收客户进程的连接请求,为套接字建立一个连接请求侦听队列。处理可能同时出现的几个连接请求。 if (listen(sockfd, lisnum) == -1) { perror("listen"); exit(1); } while (1) { /*需要再次接收用户名*/ flag = 1; printf("\n----等待新的连接到来开始新一轮聊天……\n"); len = sizeof(struct sockaddr); //协议地址长度 //int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);用于服务器进程接收客户进程的连接请求。函数返回值为新生成的用于连接的套接字(四元组形成)。 if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) { perror("accept"); exit(errno); } else printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);//ntohs 将一个无符号短整形数从网络字节顺序转换为主机字节顺序。 send(new_fd, hostname, strlen(hostname) - 1, 0); /* 开始处理每个新连接上的数据收发 */ printf("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"); while (1) { /* 把集合清空 */ FD_ZERO(&rfds); /* 把标准输入句柄0加入到集合中 */ FD_SET(0, &rfds); maxfd = 0; /* 把当前连接句柄new_fd加入到集合中 */ FD_SET(new_fd, &rfds); if (new_fd > maxfd) maxfd = new_fd; /* 设置最大等待时间 */ tv.tv_sec = 1; tv.tv_usec = 0; /* 开始等待 */ retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("将退出,select出错! %s", strerror(errno)); break; } else if (retval == 0) { /* printf ("没有任何消息到来,用户也没有按键,继续等待……\n"); */ continue; } else { if (FD_ISSET(0, &rfds)) { /* 用户按键了,则读取用户输入的内容发送出去 */ bzero(buf, MAXBUF + 1); //标准输入 char *fgets(char *buf, int n, FILE *fp); 从文件流读入一行。 fgets(buf, MAXBUF, stdin); //strncasecmp()用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异,若参数s1和s2字符串相同则返回0 s1若大于s2则返回大于0的值 s1若小于s2则返回小于0的值 if (!strncasecmp(buf, "quit", 4)) { printf("自己请求终止聊天!\n"); break; } //int send ( int sd, const void *msg, size_t len, int flags );msg:指向用于发送/接收数据的缓冲区的指针。len: 是缓冲区的字节数。 len = send(new_fd, buf, strlen(buf) - 1, 0); // hostname[len]='\0'; // slen = strlen(hostname); // hostname[slen-1] = '\0'; if (len > 0) printf ("%s的消息: \'%s\'发送成功,共发送了%d个字节!\n", hostname, buf, len); else { printf ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno)); break; } } if (FD_ISSET(new_fd, &rfds)) { if(flag == 1) { /*接收客户端用户名*/ bzero(usrname, MAXBUF + 1); recv(new_fd, usrname, MAXBUF, 0); flag = 0; } else { /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */ bzero(buf, MAXBUF + 1); /* 接收客户端的消息 */ //int recv ( int sd, void *msg, size_t len, int flags ); len = recv(new_fd, buf, MAXBUF, 0); if (len > 0) printf ("收到来自%s的消息:'%s',共%d个字节的数据\n", usrname, buf, len); else { if (len < 0) printf ("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); else printf("对方退出了,聊天终止\n"); break; } } } } } close(new_fd); /* 处理每个新连接上的数据收发结束 */ printf("还要和其它连接聊天吗?(no->退出)"); fflush(stdout);//清除文件缓冲区,文件以写方式打开时将缓冲区内容写入文件.原型:int fflush(FILE *stream) bzero(buf, MAXBUF + 1); fgets(buf, MAXBUF, stdin); if (!strncasecmp(buf, "no", 2)) { flag = 0; printf("终止聊天!\n"); break; } } close(sockfd);//关闭套接字, 发送队列中的余留数据, 终止TCP连接。 return 0; } |
#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #define MAXBUF 1024 //int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout); /*select的第一个参数是文件描述符集中要被检测的比特数,这个值必须至少比待检测的最大文件描述符大1; 参数readfds指定了被读监控的文件描述符集; 参数 writefds指定了被写监控的文件描述符集; 参数exceptfds指定了被例外条件监控的文件描述符集。 参数timeout起了定时器的作用:到了指定的时间,无论是否有设备准备好,都返回调用。*/ /************关于本文档******************************************** // *filename: ssync-client.c *purpose: 演示网络异步通讯,这是客户端程序 *wrote by: zhoulifa(zhoulifa@163 Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言 *date time:2007-01-25 21:32 *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途 * 但请遵循GPL *Thanks to: Google.com *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力 * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献! *修改、添加注释 by: nickolas *********************************************************************/ int main(int argc, char **argv) { int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; fd_set rfds; struct timeval tv; int retval, maxfd = -1; char usrname[MAXBUF + 1],hostname[MAXBUF + 1]; int flag = 1; printf("请输入您的用户名:"); bzero(hostname, MAXBUF + 1); fgets(hostname, MAXBUF, stdin); if (argc != 3) { printf ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息", argv[0], argv[0]); exit(0); } /* 创建一个 socket 用于 tcp 通信 */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } /* 初始化服务器端(对方)的地址和端口信息 */ bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); //int inet_aton(const char *cp, struct in_addr *inp); //inet_aton() 转换网络主机地址cp为二进制数值,并存储在struct in_addr结构中,即第二个参数*inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。 if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } /* 连接服务器 */ if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(errno); } /*发用户名给服务器*/ send(sockfd, hostname, strlen(hostname) - 1, 0); printf ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"); while (1) { /* 把集合清空 */ FD_ZERO(&rfds); /* 把标准输入句柄0加入到集合中 */ FD_SET(0, &rfds); maxfd = 0; /* 把当前连接句柄sockfd加入到集合中 */ FD_SET(sockfd, &rfds); if (sockfd > maxfd) maxfd = sockfd; /* 设置最大等待时间 */ tv.tv_sec = 1; tv.tv_usec = 0; /* 开始等待 */ retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("将退出,select出错! %s", strerror(errno)); break; } else if (retval == 0) { /* printf ("没有任何消息到来,用户也没有按键,继续等待……\n"); */ continue; } else { if (FD_ISSET(sockfd, &rfds)) { if(flag == 1) { /*接收服务器端用户名*/ bzero(usrname, MAXBUF + 1); recv(sockfd, usrname, MAXBUF, 0); flag = 0; } else { /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */ bzero(buffer, MAXBUF + 1); /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */ len = recv(sockfd, buffer, MAXBUF, 0); if (len > 0) printf("收到来自%s的消息:'%s',共%d个字节的数据\n", usrname, buffer, len); else { if (len < 0) printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); else printf("对方退出了,聊天终止!\n"); break; } } } if (FD_ISSET(0, &rfds)) { /* 用户按键了,则读取用户输入的内容发送出去 */ bzero(buffer, MAXBUF + 1); fgets(buffer, MAXBUF, stdin); if (!strncasecmp(buffer, "quit", 4)) { printf("自己请求终止聊天!\n"); break; } /* 发消息给服务器 */ len = send(sockfd, buffer, strlen(buffer) - 1, 0); buffer[len]='\0'; // slen = strlen(hostname); // hostname[slen-1] = '\0'; if (len < 0) { printf ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno)); break; } else printf("%s的消息:%s发送成功,共发送了%d个字节!\n", hostname, buffer, len); } } } /* 关闭连接 */ close(sockfd); return 0; } |