Linux网络编程-TCP网络编程基础(容易的server/client模型)-编程-IT技术博客

Linux网络编程-TCP网络编程基础(容易的server/client模型)

时间:2019-05-18 07:23:33 浏览: 字体: 放大 缩小


Linux网络编程--TCP网络编程基础(简单的server/client模型)

本文主要讲解C/S模型,对服务器端和客户端的流程和函数的使用进行解析,以及网络编程中对信号的处理,特别是由于连接关闭而产生的SIGPIPE信号和终止进程而产生的SIGINT信号,当然截取信号并进行处理是程序稳定性的基本要求。
TCP网络编程中常用的函数主要有:socket(),bind(),listen(),accept(),read(),write(),connect(),close();服务器端和客户端使用不同的函数,流程的差异也是很大;其中服务器端的程序设计需要依次调用socket(),bind(),listen(),accept(),close()函数,客户端程序设计需要依次调用socket(),connect(),close()函数。那么这些函数的具体定义和使用方法就不再细讲,网上随便一搜很多很多,最好是使用官方的man手册进行查询和使用。

下面主要介绍一个简单的基于TCP协议的服务器/客户端的例子,通过本例中代码和程序构建过程的了解,学习者可以对TCP协议的服务器、客户端程序的设计方法和过程有所了解,能够进一步编写自己的程序。
功能描述:程序分为服务器端和客户端,客户端连接服务器之后从标准输入设备读取输入的字符发给服务器;服务器端收到字符串后,发送接受到的总字符数给客户端;客户端将接收到的服务端的信息打印到标准输出。框架图如下:
这里写图片描述

服务器网络程序:tcp_server.c

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>

extern void sig_proccess(int signo);

#define PORT 8888 /* 侦听端口地址 */
#define BACKLOG 2 /* 侦听队列长度 */
int main(int argc, char *argv[])
{
    int ss,sc;      /* ss为服务器的socket描述符,sc为客户端的socket描述符 */
    struct sockaddr_in server_addr; /* 服务器地址结构 */
    struct sockaddr_in client_addr; /* 客户端地址结构 */
    int err;    /* 返回值 */
    pid_t pid;  /* 分叉的进行id */

    signal(SIGINT, sig_proccess);
    signal(SIGPIPE, sig_proccess);


    /* 建立一个流式套接字 */
    ss = socket(AF_INET, SOCK_STREAM, 0);
    if(ss < 0){/* 出错 */
        printf("socket error\n");
        return -1;  
    }   

    /* 设置服务器地址 */
    bzero(&server_addr, sizeof(server_addr));   /* 清0 */
    server_addr.sin_family = AF_INET;           /* 协议族 */
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */
    server_addr.sin_port = htons(PORT);         /* 服务器端口 */

    /* 绑定地址结构到套接字描述符 */
    err = bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if(err < 0){/* 出错 */
        printf("bind error\n");
        return -1;  
    }

    /* 设置侦听 */
    err = listen(ss, BACKLOG);
    if(err < 0){/* 出错 */
        printf("listen error\n");
        return -1;  
    }

    /* 主循环过程 */
    for(;;) {
        int addrlen = sizeof(struct sockaddr);
        /* 接收客户端连接 */
        sc = accept(ss, (struct sockaddr*)&client_addr, &addrlen);
        if(sc < 0){     /* 出错 */
            continue;   /* 结束本次循环 */
        }   

        /* 建立一个新的进程处理到来的连接 */
        pid = fork();       /* 分叉进程 */
        if( pid == 0 ){     /* 子进程中 */
            close(ss);      /* 在子进程中关闭服务器的侦听 */
            process_conn_server(sc);/* 处理连接 */
        }else{
            close(sc);      /* 在父进程中关闭客户端的连接 */
        }
    }
}

客户网络程序:tcp_client.c

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>

extern void sig_proccess(int signo);
extern void sig_pipe(int signo);
static int s;
void sig_proccess_client(int signo)
{
    printf("Catch a exit signal\n");
    close(s);
    exit(0);    
}

#define PORT 8888 /* 侦听端口地址 */
int main(int argc, char *argv[])
{

    struct sockaddr_in server_addr; /* 服务器地址结构 */
    int err;/* 返回值 */

    signal(SIGINT, sig_proccess);
    signal(SIGPIPE, sig_pipe);

    /* 建立一个流式套接字 */
    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s < 0){/* 出错 */
        printf("socket error\n");
        return -1;  
    }   

    /* 设置服务器地址 */
    bzero(&server_addr, sizeof(server_addr));       /* 清0 */
    server_addr.sin_family = AF_INET;               /* 协议族 */
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */
    server_addr.sin_port = htons(PORT);             /* 服务器端口 */

    /* 将用户输入的字符串类型的IP地址转为整型 */
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr); 
    /* 连接服务器 */
    connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
    process_conn_client(s); /* 客户端处理过程 */
    close(s);   /* 关闭连接 */
}

服务端和客户端中对字符串以及信号处理功能的代码:tcp_process.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

/* 服务器对客户端的处理 */
void process_conn_server(int s)
{
    ssize_t size = 0;
    char buffer[1024];  /* 数据的缓冲区 */

    for(;;){/* 循环处理过程 */
        /* 从套接字中读取数据放到缓冲区buffer中 */
        size = read(s, buffer, 1024);   
        if(size == 0){/* 没有数据 */
            return; 
        }

        /* 构建响应字符,为接收到客户端字节的数量 */
        sprintf(buffer, "%d bytes altogether\n", size);
        write(s, buffer, strlen(buffer)+1);/* 发给客户端 */
    }   
}

/* 客户端的处理过程 */
void process_conn_client(int s)
{
    ssize_t size = 0;
    char buffer[1024];  /* 数据的缓冲区 */

    for(;;){/* 循环处理过程 */
        /* 从标准输入中读取数据放到缓冲区buffer中 */
        size = read(0, buffer, 1024);
        if(size > 0){/* 读到数据 */
            write(s, buffer, size);     /* 发送给服务器 */
            size = read(s, buffer, 1024);/* 从服务器读取数据 */
            write(1, buffer, size);     /* 写到标准输出 */
        }
    }   
}

void sig_proccess(int signo)
{
    printf("Catch a exit signal\n");
    exit(0);    
}

void sig_pipe(int sign)
{
    printf("Catch a SIGPIPE signal\n");

    /* 释放资源 */  
}

程序的编译运行文件:Makefile


CC = gcc
all:client server

client:tcp_process.o tcp_client.o
    $(CC) -o client tcp_process.o tcp_client.o
server:tcp_process.o tcp_server.o
    $(CC) -o server tcp_process.o tcp_server.o
clean:
    rm -f  *.o

版权声明:本文为博主原创文章,未经博主允许不得转载。

标签: