流式(TCP)套接字客户端/服务器编程 (多线程并发服务器)

  • Post author:
  • Post category:其他


实现一个基本的流式套接字客户端/服务器通信程序,客户端和服务器按如下步骤交互:

(1)客户端向服务器发出日期时间请求字符串,如:%D %Y %A %T等。

(2)服务器从网络接收到日期时间请求字符串后,根据字符串格式生成对应的日期时间值返回给客户端。

  1. //TCP

    服务器

    /*


    用法

    :./server ip port



    说明

    :

    该流式套接字服务器程序工作于多线程模式,根据客户端发来的请求格式字符串,服务器回应对应的日期和时间

    */

    #include

    <stdio.h>


    #include

    <unistd.h>


    #include

    <stdlib.h>

    #include

    <errno.h>


    #include

    <string.h>

    #include

    <time.h>


    #include

    <sys/socket.h>


    #include

    <sys/types.h>


    #include

    <arpa/inet.h>


    #include

    <pthread.h>

    #define BUFSIZE

    1024

    #define BACKLOG

    128

    static


    void

    bail(

    const


    char

    *on_what)

    {



    fputs

    (

    strerror

    (


    errno


    ),


    stderr


    );


    fputs

    (

    “: ”

    ,

    stderr

    );


    fputs

    (on_what,

    stderr

    );


    fputc

    (

    ‘\n’

    ,

    stderr

    );


    exit

    (

    1

    );

    }

    time_t

    td;

    struct



    tm


    tm;


    struct

    TmpSockt

    {


    int

    connfd;


    sockaddr_in

    client;

    };


    void

    *start_routine(

    void

    *arg)

    {


    char

    reqBuf[

    BUFSIZE

    ];


    char

    dtfmt[

    BUFSIZE

    ];


    struct


    TmpSockt

    *temp=(

    TmpSockt

    *)arg;


    while

    (

    1

    )

    {




    //

    读取客户端发来的日期时间请求,若客户端没有发送请求,则服务器将阻塞


    long

    bytes=

    read

    (temp->

    connfd

    , reqBuf,

    sizeof

    (reqBuf));


    if

    (bytes<

    0

    )


    bail

    (

    “read()”

    );




    //

    服务器检查客户端是否关闭了套接字,此时

    read

    操作返回

    0(EOF)




    //

    如果客户端关闭了其套接字,则服务器将执行

    close

    结束此连接,然后开始接收下一个客户端的连接请求


    if

    (bytes==

    0

    )

    {




    printf


    (

    “The %ld thread exit…\n”

    ,(


    long


    )


    pthread_self


    ());


    close

    (temp->

    connfd

    );


    delete

    temp;


    pthread_exit

    (

    NULL

    );


    break

    ;

    }




    //

    向请求字符串尾添加

    NULL

    字符构成完整的请求日期时间字符串

    reqBuf[bytes]=

    0

    ;




    //

    获取服务器当前日期和时间


    time

    (&

    td

    );


    tm

    =*

    localtime

    (&

    td

    );




    //

    根据请求日期字符串的格式串生成应答字符串

    ,

    不清楚的可以查一下这个函数


    strftime

    (dtfmt,

    sizeof

    (dtfmt), reqBuf, &

    tm

    );




    //

    将格式化结果发送给客户端

    bytes=

    write

    (temp->

    connfd

    , dtfmt,

    strlen

    (dtfmt));


    if

    (bytes<

    0

    )


    bail

    (

    “write()”

    );

    }



    return


    NULL

    ;

    }


    int

    main(

    int

    argc,

    char

    **argv)

    {


    int

    sockfd,connectfd;

    //


    描述符


    pthread_t

    thread;


    TmpSockt

    *sockt;


    int

    portnumber;

    //


    端口号


    struct


    sockaddr_in

    server;

    //


    服务器地址信息


    struct


    sockaddr_in

    client;

    //


    客户端地址信息


    socklen_t

    sin_size;


    if

    ((portnumber=

    atoi

    (argv[

    2

    ]))==-

    1

    )

    {


    fprintf

    (

    stderr

    ,




    格式错误




    );


    exit

    (

    1

    );

    }


    if

    ((sockfd=

    socket

    (

    AF_INET

    ,

    SOCK_STREAM

    ,

    0

    ))==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “socket error %s”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }



    //

    设置套接字选项为

    SO_REUSEADDR




    int


    opt=

    SO_REUSEADDR

    ;


    setsockopt

    (sockfd,

    SOL_SOCKET

    ,

    SO_REUSEADDR

    , &opt,

    sizeof

    (opt));


    bzero

    (&server,

    sizeof

    (server));

    server.

    sin_family

    =

    AF_INET

    ;

    server.

    sin_port

    =

    htons

    (portnumber);


    if

    (!

    inet_aton

    (argv[

    1

    ], &server.

    sin_addr

    ))

    {




    bail


    (

    “bad address”

    );

    }




    //

    绑定套接字到相应地址


    if

    (

    bind

    (sockfd, (

    struct


    sockaddr

    *)&server,

    sizeof

    (server))==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “bind error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }




    //

    监听网络连接


    if

    (

    listen

    (sockfd,

    BACKLOG

    )==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “Listen error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }

    sin_size=

    sizeof

    (

    struct


    sockaddr_in

    );




    printf


    (

    “waiting for the client’s request…\n”

    );


    while

    (

    1

    )

    {


    if

    ((connectfd=

    accept

    (sockfd, (

    struct


    sockaddr

    *)&client, &sin_size))==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “accept error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }




    fprintf


    (


    stdout


    ,

    “Server got connection from %s\n”

    ,


    inet_ntoa


    (client.


    sin_addr


    ));

    sockt=

    new


    TmpSockt

    ;

    sockt->

    connfd

    =connectfd;


    memcpy

    ((

    void

    *)&sockt->

    client

    , &client,

    sizeof

    (client));




    //

    产生线程

    ,

    并执行线程函数,处理客服端请求


    if

    (

    pthread_create

    (&thread,

    NULL

    ,

    start_routine

    , (

    void

    *)sockt)==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “pthread_creat() error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }

    }


    close

    (sockfd);

    //


    关闭监听


    return


    0

    ;

    }

客户端:

  1. //TCP

    客户端

    /*


    用法:

    ./client hostname port



    说明:本程序使用

    TCP

    连接和

    TCP

    服务器通信,当连接建立后,向服务器发送如下格式字符串



    格式字符串示例:

    (1) %D

    (2) %A %D %H:%M:%S

    (3) %A

    (4) %H:%M:%S

    (5)…

    */

    #include

    <stdio.h>


    #include

    <stdlib.h>


    #include

    <unistd.h>

    #include

    <errno.h>


    #include

    <string.h>

    #include

    <netdb.h>


    #include

    <sys/types.h>

    #include

    <time.h>


    #include

    <sys/socket.h>


    #include

    <arpa/inet.h>

    #define BUFSIZE

    1024

    #define backlog

    128


    //


    等待队列大小

    static


    void

    bail(

    const


    char

    *on_what)

    {



    fputs

    (

    strerror

    (


    errno


    ),


    stderr


    );


    fputs

    (

    “: ”

    ,

    stderr

    );


    fputs

    (on_what,

    stderr

    );


    fputc

    (

    ‘\n’

    ,

    stderr

    );


    exit

    (

    1

    );

    }


    int

    main(

    int

    argc,

    char

    *argv[])

    {


    int

    sockfd;

    //


    客户端套接字


    char

    buf[

    BUFSIZE

    ];


    struct


    sockaddr_in

    server_addr;


    int

    portnumber;


    long

    nbytes;


    long

    z;


    char

    reqBuf[

    BUFSIZE

    ];


    if

    (argc!=

    3

    )

    {


    printf

    (




    输入格式错误


    \n”

    );


    exit

    (

    1

    );

    }


    if

    ((portnumber=

    atoi

    (argv[

    2

    ]))<

    0

    )

    {




    fprintf


    (


    stderr


    ,

    “Usage:%s hostname portnumber\a\n”

    ,argv[


    0


    ]);


    exit

    (

    1

    );

    }




    //

    创建客户端套接字


    if

    ((sockfd=

    socket

    (

    PF_INET

    ,

    SOCK_STREAM

    ,

    0

    ))==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “Socket error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }




    //

    创建服务器地址


    memset

    (&server_addr,

    0

    ,

    sizeof

    (server_addr));

    server_addr.

    sin_family

    =

    AF_INET

    ;

    server_addr.

    sin_port

    =

    htons

    (portnumber);


    if

    (!

    inet_aton

    (argv[

    1

    ], &server_addr.

    sin_addr

    ))

    {




    bail


    (

    “bad address”

    );

    }




    //

    连接服务器


    if

    (

    connect

    (sockfd, (

    struct


    sockaddr

    *)(&server_addr),

    sizeof

    (server_addr))==-

    1

    )

    {




    fprintf


    (


    stderr


    ,

    “connect error:%s\a\n”

    ,


    strerror


    (


    errno


    ));


    exit

    (

    1

    );

    }




    printf


    (

    “connected to server %s\n”

    ,


    inet_ntoa


    (server_addr.


    sin_addr


    ));




    //

    客户端主循环输入

    “quit”

    退出


    for

    (; ; )

    {




    //

    提示输入日期请求格式字符串




    fputs


    (

    “\nEnter fotmat string(^D or ‘quit’ to exit):”

    ,


    stdout


    );


    if

    (!

    fgets

    (reqBuf,

    sizeof

    (reqBuf),

    stdin

    ))

    {


    printf

    (

    “\n”

    );


    break

    ;

    }




    //

    为日期时间请求字符串添加

    NULL

    字符作为结尾,另外同时去掉末尾的换行符

    z=

    strlen

    (reqBuf);


    if

    (z>

    0

    && reqBuf[–z]==

    ‘\n’

    )

    reqBuf[z]=

    0

    ;


    if

    (z==

    0

    )

    //


    客户端仅键入


    Enter


    continue

    ;




    //


    输入


    ‘quit’


    退出


    if

    (!

    strcasecmp

    (reqBuf,

    “QUIT”

    ))

    //


    忽略大小写比较

    {




    printf


    (

    “press any key to end client.\n”

    );


    getchar

    ();


    break

    ;

    }




    //

    发送日期时间请求字符串到服务器,注意请求信息中去掉了

    NULL

    字符

    z=

    write

    (sockfd, reqBuf,

    sizeof

    (reqBuf));




    printf


    (

    “client has sent ‘%s’ to the sever\n”

    ,reqBuf);


    if

    (z<

    0

    )


    bail

    (

    “write()”

    );




    //

    从客户端套接字中读取服务器发回的应答


    if

    ((nbytes=

    read

    (sockfd,buf,

    sizeof

    (buf)))==-

    1

    )

    {


    fprintf

    (

    stderr

    ,

    “read error:%s\n”

    ,

    strerror

    (

    errno

    ));


    exit

    (

    1

    );

    }




    //

    若服务器由于某种原因关闭了连接,则客户端需要处理此事件


    if

    (nbytes==

    0

    )

    {




    printf


    (

    “server hs closed the socket.\n”

    );




    printf


    (

    “press any key to exit…\n”

    );


    getchar

    ();


    break

    ;

    }

    buf[nbytes]=

    ‘\0’

    ;




    //


    输出日期时间结果


    printf

    (

    “result from %s port %u:\n\t’%s’\n”

    ,

    inet_ntoa

    (server_addr.

    sin_addr

    ),(

    unsigned

    )

    ntohs

    (server_addr.

    sin_port

    ),buf);

    }


    close

    (sockfd);


    return


    0

    ;

    }



版权声明:本文为u014634338原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。