Chinaunix首页 | 论坛 | 博客
  • 博客访问: 423611
  • 博文数量: 115
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 393
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-26 12:10
个人简介

踏实做事,认真做人

文章分类

全部博文(115)

文章存档

2017年(1)

2016年(2)

2015年(14)

2014年(63)

2013年(35)

分类: C/C++

2014-05-27 17:37:28

这里我们用前面所介绍的基础知识编写一个完整的TCP客户服务器程序示例。我们要实现的是一个回射服务器:
1)客户从标准输入读入一行文本,并发送给服务器
2)服务器从网络输入读入这行文本,并回射给客户
3)客户接受到回射文本后,将其显示在标准输出上。

tcp服务器源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#define    SERV_PORT    5678
#define MAX_LINE    1024


void str_echo(int sockfd)
{
    ssize_t n;
    char buf[MAX_LINE] = {0};
    while( (n = read(sockfd, buf, MAX_LINE)) > 0)
        write(sockfd, buf, n);
    if( n < 0)
        printf("read error.\n");
}

int main(int argc, char **argv)
{
    int listenfd, connfd;
    pid_t    childpid;
    socklen_t    cli_len;
    struct sockaddr_in    cliaddr,servaddr;
    
    cli_len = sizeof(cliaddr);
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0)
    {
        printf("create socket error!\n");
        exit(1);
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("bind error!\n");
        exit(1);
    }
    listen(listenfd, 5);
    for(;;)
    {
        connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
        if( (childpid = fork()) == 0)/*child process*/
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);    /*parent close connected socket*/
    }
    return 0;
}

tcp客户端源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#define    SERV_PORT    5678
#define MAX_LINE    1024


void str_cli(FILE *fp, int sockfd)
{
    char sendline[MAX_LINE], recvline[MAX_LINE];
    int n;
    while( fgets(sendline, MAX_LINE, fp) != NULL)
    {
        write(sockfd, sendline, strlen(sendline));
        if( (n = read(sockfd, recvline, MAX_LINE)) == 0)
            exit(0);
        recvline[n]='\0';
        fputs(recvline, stdout);
    }
}
int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in    servaddr;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        printf("create socket error!\n");
        exit(1);
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    str_cli(stdin, sockfd);    
    return 0;
}

源码中需要注意的地方就是tcp服务器代码中的一段:
        if( (childpid = fork()) == 0)/*child process*/
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
这里在子进程中,首先将listenfd关闭,因为fork函数后,子进程会获得父进程资源的副本,而子进程中并不需要listenfd,直接关闭免的出麻烦。在str_echo调用完成后,并没有使用close(connfd),而是直接调用exit函数。这里我们看下exit函数究竟会做什么。
exit调用之后,会调用退出处理函数,这里就是一些对资源的释放,包括已打开的套接字。

直接在linux上编译运行服务器端:


服务端启动后会在5678端口上监听:

然后启动客户端:
并在输入端输入hello,world<回车>后,会显示hello,world,此时我们再看下网络连接状况:
可以看出,服务端在接收到一个连接请求后,fork了一个子进程进行处理,主进程仍然在监听。同时,客户端和服务器端已经建立了连接。
当我们在客户端按下ctrl+d退出之后,再看下网络连接:
服务器的主进程仍然在监听,而客户端的socket套接字的tcp状态处于time_wait状态,至于为什么会到time_wait状态可以看下我以前的博文。此时,服务器的子进程已经退出,我们可以看下进程的状态:
stat中Z表示僵死。可以看出tcp主进程阻塞与accept函数,子进程虽然退出,但是处于僵死状态。这是因为,子进程退出时,会向父进程发送一个SIGCHLD的信号,一般情况下,父进程接收到该信号后,负责对子进程进行清理工作。但是我们在父进程中并没有处理该信号,于是,子进程便处于僵死状态,所占用的资源并没释放。其实,这段代码并未对异常进程处理,所以健壮性方面很差。

阅读(782) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~