天天看點

微信h5棋牌架設多線程,多程序伺服器的實作

微信h5棋牌架設(aqiulian.com)多線程,多程序伺服器的實作,完整的微信h5棋牌架設,搭建教程聯系方式: QQ:212303635.

socket

在TCP/IP協定中,“IP位址+TCP/UDP端口号”表示唯一網絡通信中的一個程序,IP位址+端口号稱為socket。 

注意: 

位元組序有大端和小端,在兩台使用不同位元組序的主機上通信,為了讓兩台主機間能正确的通信,發送端總是把位元組序轉成大端位元組序資料後在發送,接收方知道接受的一定是大端位元組序,然後根據自己的位元組序進行轉化就不會出錯。

調用函數

int socket(int domain, int type, int protocol)

  1. 第一個參數是告訴系統使用哪個底層協定族。對于TCP/IP協定族該參數應該設定為AF_INET/PF_INET用于IPV4,兩個值相同可以混用。
  2. 第二個參數 SOCK_STREAM表示傳輸層使用TCP,SOCK_DGRAM表式UDP。
  3. 第三個參數通常都設為0,使用預設協定。 

    int bind(int sockfd, struct sockaddr* my_addr, socklen_t len)  struct sockaddr_in  {  sa_family_t sin_family; //位址族; AF_INET  u_int16_t sin_port; //端口号  struct in_addr sin_addr; //IPV4位址結構體  }  struct in_addr  {  u_int32_t s_addr; //IPV4位址  } 

    bind是将my_addr所指的socket配置設定到未命名的sockfd檔案描述符。

int listen(int sockfd, int backlog)

socket被命名之後不能馬上連接配接,需要listen建立一個監聽隊列,sockfd是指被監聽的socket,backlog是指處于連接配接狀态和socket的上限。

int accept(int sockfd, struct sockaddr*addr, socklen_t *len)

從listen監聽隊列中接受一個連接配接。sockfd是執行過linten接受監聽的socket。

單程序的server

/*************************************************************************
    > File Name: tcp_server.c
    > Author: weierxiao
    > Mail: [email protected] 
    > Created Time: Sat 08 Jul 2017 05:15:29 PM CST
 ************************************************************************/

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
    int sock = socket(AF_INET, SOCK_STREAM,0);
    if (sock < 0)
    {
        printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
    }
    struct sockaddr_in server_sock;
    struct sockaddr_in client_sock;
    bzero(&server_sock, sizeof(server_sock));

    server_sock.sin_family = AF_INET;
    server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sock.sin_port = htons(_PORT_);

    if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
    {
        printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 1;
    }

    if (listen(sock, _BACKLOG_)< 0)
    {
        printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 2;
    }

    printf("bind and listen success, wait accept!\n");

    while (1)
    {
        socklen_t len = 0;
        int client_socket = accept(sock, (struct sockaddr*)&client_sock,&len);
        if (client_socket < 0)
        {
            printf("accept  error, errno is %d, errstring is %s\n", errno, strerror(errno));
            close(sock);
            return 3;
        }

        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip, 0, sizeof(buf_ip));
        inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));

        printf("get connection :ip is  %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );

        while (1)
        {
            char buf[1024];
            memset(buf, 0, sizeof(buf));

            read(client_socket, buf, sizeof(buf));
            printf("client# : %s\n", buf);
            printf("server# :");
            memset(buf, 0, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1 ]= \'\0\';
            write(client_socket, buf, strlen(buf)+1);
            printf("please wait ...\n");

        }
    }

    close(sock);
    return 0;

    }           

client用戶端

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

#define SERVER_PORT 9999
#define SERVER_IP "192.168.2.103"

int main(int argc, char *argv[])
{

    if (argc != 2)
    {
        printf("Usage : client IP\n");
        return 1;
    }

    char *ip = argv[1];
    char buf[1024];
    memset(buf,0,sizeof(buf));

    struct sockaddr_in server_sock;
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
    }

    bzero(&server_sock, sizeof(server_sock));
    server_sock.sin_family = AF_INET;
    inet_pton(AF_INET, SERVER_IP, &server_sock.sin_addr);
    server_sock.sin_port = htons(SERVER_PORT);

    int ret = connect(sock, (struct sockaddr*)& server_sock, sizeof(server_sock));

    if (ret < 0)
    {
        printf("connect error, errno is %d, errstring is %s\n", errno, strerror(errno));
        return 1;
    }

    printf("connect sucess!\n");
    while(1)
    {
        printf("client# :");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = \'\0\';
        write(sock, buf, sizeof(buf));
        if (strncasecmp(buf,"quit", 4) == 0)
        {
            printf("quit !\n");
            break;
        }
        printf("please wait\n");
        read(sock, buf, sizeof(buf));
        printf("server #:%s\n", buf);

    }
    close(sock);
    return 0;
}           

我主機的ip位址是192.168.2.103. 

運作server 

用指令netstat _nltp 可以檢視目前主機的tcp服務 

我綁定的是9999端口,ip位址還沒确定,運作用戶端進行通信 

多程序伺服器

将通信部分讓子程序去處理,其他過程讓父程序來處理。但是問題來了,隻要伺服器一直運作,我們的父程序就不會結束,而子程序随時可能結束,這樣一來子程序就變成了僵屍程序,那麼這個問題怎麼處理呢?其實處理這個問題非常巧妙,我們讓子程序再fork一次,得到一個孫子程序,然後結束子程序,這樣孫子程序就成了孤兒程序,它會被1号程序回收,在這個過程中并沒有産生僵屍程序,這個問題就解決了。

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

#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
    int sock = socket(AF_INET, SOCK_STREAM,0);
    if (sock < 0)
    {
        printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
    }
    struct sockaddr_in server_sock;
    struct sockaddr_in client_sock;
    bzero(&server_sock, sizeof(server_sock));

    server_sock.sin_family = AF_INET;
    server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sock.sin_port = htons(_PORT_);

    if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
    {
        printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 1;
    }

    if (listen(sock, _BACKLOG_)< 0)
    {
        printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 2;
    }

    printf("bind and listen success, wait accept!\n");

    while (1)
    {
        socklen_t len = 0;
        int client_socket = accept(sock, (struct sockaddr*)&client_sock,&len);
        if (client_socket < 0)
        {
            printf("accept  error, errno is %d, errstring is %s\n", errno, strerror(errno));
            close(sock);
            return 3;
        }

        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip, 0, sizeof(buf_ip));
        inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));

        printf("get connection :ip is  %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );

        pid_t id = fork();
        if (id<0)
        {
            perror("fork");
        }
        else if(id == 0)
        {
            close(sock);
            pid_t idd = fork();
            if (idd < 0)
            {
                perror("second fork");
                exit(5);
            }
            else if (idd == 0)
            {
                    while (1)
                    {
                        char buf[1024];
                        memset(buf, 0, sizeof(buf));

                        read(client_socket, buf, sizeof(buf));
                        printf("client# : %s\n", buf);
                        printf("server# :");
                        memset(buf, 0, sizeof(buf));
                        fgets(buf, sizeof(buf), stdin);
                        buf[strlen(buf) - 1 ]= \'\0\';
                        write(client_socket, buf, strlen(buf)+1);
                        printf("please wait ...\n");

                    }
            }
            else 
            {
                exit(6);
            }
        }
        else
        {
            close(client_socket);
            waitpid(id, NULL,0);
        }

    }
    close(sock);
    return 0;

    }           

多線程伺服器

多線程伺服器就是将通信部分讓一個線程去處理,為了避免線程退出時整個程序退出,我們将線程分離出去。

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#define _PORT_ 9999
#define _BACKLOG_ 10

void request(void* arg)
{
    int new_fd = (int )arg;
    char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip, 0, sizeof(buf_ip));
        inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));

        printf("get connection :ip is  %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );

        while (1)
        {
            char buf[1024];
            memset(buf, 0, sizeof(buf));

            read(client_socket, buf, sizeof(buf));
            printf("client# : %s\n", buf);
            printf("server# :");
            memset(buf, 0, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1 ]= \'\0\';
            write(client_socket, buf, strlen(buf)+1);
            printf("please wait ...\n");            
        }
    }

}
int main()
{
    int sock = socket(AF_INET, SOCK_STREAM,0);
    if (sock < 0)
    {
        printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
    }
    struct sockaddr_in server_sock;
    struct sockaddr_in client_sock;
    bzero(&server_sock, sizeof(server_sock));

    server_sock.sin_family = AF_INET;
    server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sock.sin_port = htons(_PORT_);

    if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
    {
        printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 1;
    }

    if (listen(sock, _BACKLOG_)< 0)
    {
        printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
        close(sock);
        return 2;
    }

    printf("bind and listen success, wait accept!\n");
    pthread_t id;
    pthread_create(&id, NULL, request, (void*)new_fd );
    pthread_detach(id);

    close(sock);
    return 0;

    }           

bind失敗的原因

當兩個 socket 的 address 和 port 相沖突,而你又想重用位址和端口,則舊的 socket 和新的 socket 都要已經被設定了 SO_REUSEADDR 特性,隻 有兩者之一有這個特性還是有問題的。 

SO_REUSEADDR 可以用在以下四種情況下。 

( 摘自《 Unix 網絡程式設計》卷一,即 UNPv1) 

1 、當有一個有相同本地位址和端口的 socket1 處于 TIME_WAIT 狀态時,而你啟 

動的程式的 socket2 要占用該位址和端口,你的程式就要用到該選項。 

2 、 SO_REUSEADDR 允許同一 port 上啟動同一伺服器的多個執行個體 ( 多個程序 ) 。但 

每個執行個體綁定的 IP 位址是不能相同的。在有多塊網卡或用 IP Alias 技術的機器可 

以測試這種情況。 

3 、 SO_REUSEADDR 允許單個程序綁定相同的端口到多個 socket 上,但每個 soc 

ket 綁定的 ip 位址不同。這和 2 很相似,差別請看 UNPv1 。 

4 、 SO_REUSEADDR 允許完全相同的位址和端口的重複綁定。但這隻用于 UDP 的 

多點傳播,不用于 TCP 。

微信h5棋牌架設多線程,多程式伺服器的實作