Linux网络编程一步一步学-异步通讯聊天程序select

Linux网络编程一步一步学-异步通讯聊天程序select

Client

#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.com) 周立发(http://zhoulifa.bokee.com)

Linux爱好者 Linux知识传播者 SOHO 开发者 最擅长C语言

*date time:2007-01-25 21:22

*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

* 但请遵循GPL

*Thanks to: Google.com

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!

*********************************************************************/

 

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];

    fd_set rfds;

    struct timeval tv;

    int retval, maxfd = -1;

 

    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) {

        perror("socket");

        exit(1);

    }

 

    bzero(&my_addr, sizeof(my_addr));

    my_addr.sin_family = PF_INET;

    my_addr.sin_port = htons(myport);

    if (argv[3])

        my_addr.sin_addr.s_addr = inet_addr(argv[3]);

    else

        my_addr.sin_addr.s_addr = INADDR_ANY;

 

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))

        == -1) {

        perror("bind");

        exit(1);

    }

 

    if (listen(sockfd, lisnum) == -1) {

        perror("listen");

        exit(1);

    }

 

    while (1) {

        printf

            ("/n----等待新的连接到来开始新一轮聊天……/n");

        len = sizeof(struct sockaddr);

        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);

 

        /* 开始处理每个新连接上的数据收发 */

        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);

                    fgets(buf, MAXBUF, stdin);

                    if (!strncasecmp(buf, "quit", 4)) {

                        printf("自己请求终止聊天!/n");

                        break;

                    }

                    len = send(new_fd, buf, strlen(buf) - 1, 0);

                    if (len > 0)

                        printf

                            ("消息:%s/t发送成功,共发送了%d个字节!/n",

                             buf, len);

                    else {

                        printf

                            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",

                             buf, errno, strerror(errno));

                        break;

                    }

                }

                if (FD_ISSET(new_fd, &rfds)) {

                    /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */

                    bzero(buf, MAXBUF + 1);

                    /* 接收客户端的消息 */

                    len = recv(new_fd, buf, MAXBUF, 0);

                    if (len > 0)

                        printf

                            ("接收消息成功:'%s',共%d个字节的数据/n",

                             buf, len);

                    else {

                        if (len < 0)

                            printf

                                ("消息接收失败!错误代码是%d,错误信息是'%s'/n",

                                 errno, strerror(errno));

                        else

                            printf("对方退出了,聊天终止/n");

                        break;

                    }

                }

            }

        }

        close(new_fd);

        /* 处理每个新连接上的数据收发结束 */

        printf("还要和其它连接聊天吗?(no->退出)");

        fflush(stdout);

        bzero(buf, MAXBUF + 1);

        fgets(buf, MAXBUF, stdin);

        if (!strncasecmp(buf, "no", 2)) {

            printf("终止聊天!/n");

            break;

        }

    }

 

    close(sockfd);

    return 0;

}

 

 

 

 

 

Server

#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

/************关于本文档********************************************

// *filename: ssync-client.c

*purpose: 演示网络异步通讯,这是客户端程序

*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)

Linux爱好者 Linux知识传播者 SOHO 开发者 最擅长C语言

*date time:2007-01-25 21:32

*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

* 但请遵循GPL

*Thanks to: Google.com

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!

*********************************************************************/

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;

 

    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]));

    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);

    }

 

    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)) {

                /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */

                bzero(buffer, MAXBUF + 1);

                /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */

                len = recv(sockfd, buffer, MAXBUF, 0);

                if (len > 0)

                    printf

                        ("接收消息成功:'%s',共%d个字节的数据/n",

                         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);

                if (len < 0) {

                    printf

                        ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",

                         buffer, errno, strerror(errno));

                    break;

                } else

                    printf

                        ("消息:%s/t发送成功,共发送了%d个字节!/n",

                         buffer, len);

            }

        }

    }

    /* 关闭连接 */

    close(sockfd);

    return 0;

}

 

编译用如下命令:
gcc -Wall async-server.c -o server
gcc -Wall async-client.c -o client
运行用如下命令:
./server 7838 1
./client 127.0.0.1 7838

 

===========================================================================

 

[work@db-testing-com06-vm3.db01.baidu.com net]$ ./server 7838 1

 

----等待新的连接到来开始新一轮聊天……

aaa

server: got connection from 127.0.0.1, port 32999, socket 4

 

准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方

消息:aaa

        发送成功,共发送了3个字节!

接收消息成功:'yanggang nihao',共14个字节的数据

==========================

[work@db-testing-com06-vm3.db01.baidu.com net]$ ./client 127.0.0.1 7838

 

准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方

接收消息成功:'aaa',共3个字节的数据

yanggang nihao

消息:yanggang nihao

        发送成功,共发送了14个字节!

 

===========================================================================

 

什么是异步通讯?
就是通讯任意一方可以任意发送消息,有消息来到时会收到系统提示去接收消息。

这里要用到select函数。使用步骤如下:
1
、设置一个集合变量,用来存放所有要判断的句柄(file descriptors:即我们建立的每个socket、用open打开的每个文件等)
2
、把需要判断的句柄加入到集合里
3
、设置判断时间
4
、开始等待,即select
5
、如果在设定的时间内有任何句柄状态变化了就马上返回,并把句柄设置到集合里

posted @ 2010-10-25 20:26  程序员天下  阅读(272)  评论(0编辑  收藏  举报