多进程多线程TCP服务器

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”称为(套接字)socket。在TCP协议中,建立连接的两个进程各自有一个socket来标识,这两个socket组成的socket pair就唯一标识一个连接。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。

此处使用socket API应用层编程接口来实现多进程多线程的TCP服务器。
接口函数如下:

  • 创建套接字
    这里写图片描述
    参数:
    domain:有以下参数可选IPv4使用AF_INET
    这里写图片描述
    type:服务的类型:TCP为SOCK_STREAM,UDP为SOCK_DRAM
    这里写图片描述
    protocol:一般默认为0.
    返回值:成功返回文件描述符,失败-1.
  • 套接字绑定:填充网络
    这里写图片描述
    参数:
    sockfd:创建的文件描述符(套接字)
    addrlen:结构体长度
    addr:结构体类型指针,其结构体为(在传参时需强制类型转换):
 struct sockaddr_in{
          sin_family;   //IPv4为AF_INET
          sin_port;//端口号
          sin_addr;//IP地址
          sin_pad;//填充字段
      };
  • 套接字监听,是否有other连接自己
    这里写图片描述
    参数:
    sockfd:套接字文件描述符;
    backlog:一般数值不能过大,此处代码设为10.
    返回值:成功则将套接字改为了监听套接字

    - 接收other主机连接,保存连接自己主机的信息
    这里写图片描述
    参数:
    sockfd:监听套接字
    addr:输出型参数,结构体保存客户端套接字信息
    addrlen:输入输出型参数,传入与传出结构体大小.
    返回值:成功返回一个新的套接字,真正用于服务通信等.

  • 发起连接请求
    这里写图片描述
    参数:
    sockfd:套接字文件描述符
    addr:连接的服务器socket信息;
    addrlen:结构体长度.

以上即为实现TCP服务器的主要函数:代码如下
tcp_server.c:

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

//./tcp_server 192.168.x.x 8080
static void usage(const char* proc)
{
    printf("usage:%s [server_ip] [server_port]\n",proc);
}

int startup(char* ip,int port)
{
    int sock=socket(AF_INET,SOCK_STREAM,0);   //创建套接字 
    if(sock<0)
    {
        perror("socket");
        close(sock);
        exit(2);
    }

    struct sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_port=htons(port);
    server.sin_addr.s_addr=inet_addr(ip);
    if(bind(sock,(struct sockaddr*)&server,sizeof(server))<0)    //绑定:填充网络 
    {
        perror("bind");
        close(sock);
        exit(3);
    }

    if(listen(sock,10)<0)   //监听
    {
        perror("listen");
        close(sock);
        exit(4);
    }

    return sock;  //返回监听套接字 
}

void* request(void* arg)
{
    int new_sock=(int)arg;
    while(1)
    {
        char buf[1024];
        ssize_t s=read(new_sock,buf,sizeof(buf)-1);
        if(s>0)
        {
            buf[s]=0;
            printf("get new client# %s\n",buf);
            write(new_sock,buf,strlen(buf));
        }
        else if(s==0)
        {
            printf("client close!!!\n");
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
    return (void*)0;
}

int main(int argc,char* argv[])
{
    if(argc != 3)    //命令行用法 
    {
        usage(argv[0]);
        return 1;
    }

    int listen_sock=startup(argv[1],atoi(argv[2]));  //创建监听套接字:函数三步 

    while(1)
    {
        struct sockaddr_in client;
        socklen_t addrlen=sizeof(client);
        int new_sock=accept(listen_sock,(struct sockaddr*)&client,&addrlen);  //接收客户端信息进行通信 
        if(new_sock<0)
        {
            perror("accept");
            continue;
        }

        //version 1.3
        pthread_t id;
        pthread_create(&id,NULL,request,(void*)new_sock);    //主线程监听接收客户端,创建线程为每一个客户端服务 
        pthread_detach(id);     //使线程分离,主线程不用阻塞等待,操作系统去回收 

        //version 1.2
        /*pid_t id=fork();    //即每有一个客户端连接则创建一个子进程为其服务 
        if(id<0)
        {
           perror("fork");
           close(new_sock);        
        }
        else if(id==0)  //child    子进程进行处理请求,服务 
        {
            close(listen_sock);
            pid_t _id=fork();   //在子进程再次fork,使子进程的子进程去执行服务 
            if(_id>0)
            {
                exit(0);    //子进程退出,即让父进程不会阻塞等待 
            }
            else if(_id==0)   //child->child
            {
                while(1)
                {
                    char buf[1024];
                    ssize_t s=read(new_sock,buf,sizeof(buf)-1);
                    if(s>0)
                    {
                        buf[s]=0;
                        printf("[%s:%d]# %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
                        write(new_sock,buf,strlen(buf));
                    }
                    else if(s==0)
                    {
                        printf("client close!!!\n");
                        break;
                    }
                    else
                    {
                        perror("read");
                        break;
                    }
                }
            }
            close(new_sock);
            exit(0);
        }
        else   //father   父进程进行监听接收多个客户端 
        {
            close(new_sock);
            waitpid(id,NULL,0);
        }*/


        /*verison 1.1
        //服务: 
        printf("get new client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));  //接收成功 
        while(1)
        {
            char buf[1024];
            //先从网络上读取客户端请求 
            ssize_t s=read(new_sock,buf,sizeof(buf)-1);
            if(s>0)
            {
                buf[s]=0;
                printf("client# %s\n",buf);
                //向客户端服务:写入网络 
                write(new_sock,buf,strlen(buf));
            }
            else if(s==0)   //若读取为0字节,则客户端关闭断开连接,则不在服务,再次去监听接收 
            {
                printf("client close!!!\n");
                break;
            }
            else
            {
                perror("read");
                break;
            }

        }*/
    }

    return 0;
}

tcp_client.c:

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

//./tcp_client 192.168.x.x 8080
static void usage(const char* proc)
{
    printf("usage:%s [server_ip] [server_port]\n",proc);
}

int main(int argc,char* argv[])
{
    if(argc != 3)    //命令行用法 
    {
        usage(argv[0]);
        return 1;
    }

    int sock=socket(AF_INET,SOCK_STREAM,0);    //创建客户端套接字 
    if(sock<0)
    {
        perror("socket");
        return 2;
    }

    struct sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_port=htons(atoi(argv[2]));
    server.sin_addr.s_addr=inet_addr(argv[1]);
    if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)   //客户端与服务器建立连接 
    {
        perror("connect");
        return 1;
    }

    //连接成功 
    char buf[1024];
    while(1)
    {
        //先往网络上写 
        printf("Please Enter# ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1);
        if(s>0)
        {
            buf[s-1]=0;
            write(sock,buf,strlen(buf));
        }

        //从网络上读取服务器提供的信息 
        ssize_t _s=read(sock,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]=0;
            printf("server$ %s\n",buf);
        }
    }

    //关闭sock,则断开连接 
    close(sock);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章