网站首页
关于我们
主机分类
解决方案
云计算研究
资源下载
云计算教程
留言反馈
联系我们

云计算教程

当前位置:主页 > 云计算教程 >

TCP server的实现,和多线程,多进程服务器

发布时间:2018-02-24 10:21

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: 1091868910@qq.com 
    > 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;

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

我主机的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;

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

多线程服务器

多线程服务器就是将通信部分让一个线程去处理,为了避免线程退出时整个进程退出,我们将线程分离出去。

#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;

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

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 。

网站地图|百度云Windows 2012 如何安装| Linux安装Chrome方法