首页  编辑  

一个简单的Linux TCP Server Socket编程框架,支持并发

Tags: /计算机文档/Linux & Unix/   Date Created:
简单的,高性能,支持并发的Socket编程框架,容易使用,C语言实现。

tcp_server.h
---------------------------------
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   tcp_server.h
 * Author: LYH
 *
 * Created on 2019年11月27日, 上午10:03
 */

#ifndef TCP_SERVER_H
#define TCP_SERVER_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef void (ClientFunction)(int client);
void start_tcp_server_fork(int port, ClientFunction fp);

#ifdef __cplusplus
}
#endif

#endif /* TCP_SERVER_H */
tcp_server.c
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

#include "tcp_server.h"

void sig_chld(int sig) {
    pid_t pid;
    int stat;
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d termination\n", pid);
    return;
}

void start_tcp_server_fork(int port, ClientFunction fp) {
    printf("Start TCP Server at %d...\n", port);

    int client_socket;
    struct sockaddr_in sockaddr;
    memset(&sockaddr, 0, sizeof (sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    sockaddr.sin_port = htons(port);
    
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bind(listenfd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
    listen(listenfd, 1024);
    signal(SIGCHLD, sig_chld);
    int child_pid;

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t slen = sizeof (client_addr);
        client_socket = accept(listenfd, (struct sockaddr *) &client_addr, &slen);
        printf("Client %s:%hu connected\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
        if (client_socket < 0) {
            printf("Accpet socket error: %s errno :%d\n", strerror(errno), errno);
            continue;
        }

        // Fork 子进程,防止并发阻塞,即服务器可以同时接收多个Socket连接并处理
        child_pid = fork();
        if (child_pid == 0) {
            close(listenfd); // 必须关闭,否则会留下僵尸句柄
            fp(client_socket);
            close(client_socket);
            exit(-6);
        } else if (child_pid > 0)
            close(client_socket);
    }
    close(listenfd);
}
主程序示例代码,demo.c
#include <stdio.h>
#include "tcp_server.h"

static ssize_t readch(int fd, char *ptr)
{
    static int          count = 0;
    static char*        read_ptr = 0;
    static char         read_buf[1024*4] = {0};

    if (count <= 0)
    {
    again:
        count = read(fd, read_buf, sizeof(read_buf));
        if (-1 == count)
            if (EINTR == errno)
                goto again;
            else
                return -1;
        else if (0 == count)
            return 0;
        read_ptr = read_buf;
    }
    count--;
    *ptr = *read_ptr++;
    return 1;
}

ssize_t readline(int fd, void *vptr, size_t maxlen)
{
    ssize_t         i = 0;
    ssize_t         ret = 0;
    char            ch = '\0';
    char*           ptr = NULL;

    ptr = (char *)vptr;

    for (i = 1; i < maxlen; ++i)
    {
        ret = readch(fd, &ch);
        if (1 == ret)
        {
            *ptr++ = ch;
            if ('\n' == ch)
                break;
        }
        else if (0 == ret)
        {
            *ptr = 0;
            return i-1;
        }
        else
            return -1;
    }
    *ptr = 0;
    return i;
}

void callback(int client)
{
    char buf[4096];
    write(client, "hello world\n", 12);
    while (1) {
        int len = readline(client, buf, sizeof(buf));
        if (len<0) return;
        
        write(client, "Server read: ", 13);
        write(client, buf, len);
    }
}

int main(int argc, char**argv) {
    int i;
    
    start_tcp_server_fork(8888, callback);
    return 0;
}