多進程多線程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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章