基于tcp协议的简单多并发服务器-c语言

基于tcp协议的简单多并发服务器-c语言_第1张图片

       tcp协议的基本情况在此就不详细介绍,上图是《UNIX网络编程》书里的一张图,此图直观的展现了tcp服务器和客户是如何开始通信、进行通信和结束通信的全过程。

一、套接字地址结构介绍

     首先介绍Ipv4套接字地址结构:

  

   struct in_addr {
	in_addr_t s_addr;//32位 ipv4的地址存放在这
   };
   struct sockaddr_in {
	__uint8_t	sin_len;
	sa_family_t	sin_family;
	in_port_t	sin_port;
	struct	in_addr sin_addr;
	char		sin_zero[8];
    };
    

    sin_len是为增加对OSI协议的支持而随4.3BSD-Reno添加的。

    sin_family是一个无符号短整数,使用时可以按照不同的规范选用。

    sin_port是端口号。

    sin_addr是一个结构体,该结构体内保存了ipv4地址。

    sin_zero[8]字段未曾被使用过,不过在填写这种套接字地址结构时,总是把它置为0

   为了让套接字函数的输入适应不同协议族(ipv4、ipv6等),提供了通用套接字。

   通用套接字地址结构:

 

   struct sockaddr {
	__uint8_t	sa_len;		
	sa_family_t	sa_family;	
	char		sa_data[14];	
};
    

 在使用bind()accept()等函数时需要把Ipv4套接字地址结构强制转化成通用套接字然后带入函数。例如:

 

int bind(int,struct sockaddr *,socklen_t);

二、基本套接字函数

   

      socket() 函数:

      socket()函数用于建套接字,socket()函数成功返回一个似于文件描述符的非整数,我称之套接字描述符。

     

int socket(int family, int type, int protocol);

     成功返回非数描述符,失败返-1

    其中:

    family参数指明协议族,ipv4一般用AF_INET。

    type参数指明套接字类型,有四种类型分别是字节流套接字、   数据报套接字、有序分组套接字、原始套接字。这里我们用字节流套接字,SOCK_STREAM。对另外几种感兴趣的小伙伴可以自己百度一下。

    Protocol参数是协议类型,有tcp传输协议、udp传输协议、sctp传输协议。也可以默认为0,以family和type的合,系自己赋值

     connect() 函数:

    connect()函数用于通信中的主方向被起建立接的求。于TCP套接字来,connect()函数会激TCP的三次握手。 

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    成功返回0,失败返-1

    sockfd套接字描述符,由socket()函数返回的。 
    addr
指向套接字地址构的指 
    addrlen
套接字地址结构的大小。

    bind() 函数:

    bind()函数用于将本地协议地址与socket()函数建的套接字定起来。

    
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

    成功返回0,失返回-1。 

    sockfd 套接字描述符,由前面的socket()函数成功返回。
    myaddr
 指向特定于协议的地址构的指
    addrlen
 该地构的度。

   listen()函数:

   listen()函数由TCP,主要有两个作用: 
   1.
将一个未接的套接字转换为套接字,指示内核应该接  收指向套接字的求。

   2. 指定内核应该为的套接字列的最大接个数。

 

int listen(int sockfd, int backlog);

    成功返回0,失返回-1

    sockfd 套接字描述符,由前面的socket()函数成功返回。
    backlog
 指定内核应该为的套接字排的最大接个数。

    accept()函数:

     accept()函数由TCP用,用于返回下一个已完成接。如果已完成空,阻塞。

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

    成功返回套接字描述符,失返回-1

     sockfd 内核建的新的套接字描述符,用于描述与返回的客端之接 
     cliaddr
接的端客户对协议地址。
     addrlen
 该地构的度。

     fork()函数:

pid_t fork(void);

    返回:两次返回在子程中返回0,在父程中程的id,若出错则为-1

    生成一个和父程一的子程,代替父行下面的操作,解放父程。

三、服器端代

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int     main()
{
    pid_t pid;
    int ser_socket;
    struct sockaddr_in se_addr,cl_addr;
    int addr_len = sizeof(cl_addr);
    int client;
    char buffer[4000],renew[4000];
    int datanum;
    
    if ((ser_socket = socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("socket");
        return -1;
    }
    
    bzero(&se_addr, sizeof(se_addr));
    se_addr.sin_family = AF_INET;
    se_addr.sin_port = htons(5901);
    se_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    if(bind(ser_socket,(struct sockaddr *)&se_addr,sizeof(se_addr))<0)
    {
        perror("bind");
        return -1;
    }
    
    if(listen(ser_socket, 10)<0)
    {
        perror("listen");
        return -1;
    }
    //等待请求
    while (1) {
        printf("Listening on port :%d\n",5901);
        client = accept(ser_socket, (struct sockaddr*)&cl_addr, (socklen_t *)&addr_len);
        if(client<0)
        {
            perror("accept");
            continue;
        }
        printf("\n recv client data:\n");
        printf("IP is %s\n",inet_ntoa(cl_addr.sin_addr));
        printf("port is %d\n",htons(cl_addr.sin_port));
        //进行对话,创建线程
        if((pid = fork())==0)
        {
         close(ser_socket);
        while(1)
        {
            datanum = recv(client, buffer, 4000, 0);// 收数据
            if(datanum< 0)
            {
                perror("recv");
                continue;
            }
            buffer[datanum] = '\0';
            printf("%d:say %s\n", htons(cl_addr.sin_port), buffer);
            printf("you want to say:");
            scanf("%s",renew);
            send(client, renew, strlen(renew), 0);
            if(strcmp(renew, "quit") == 0)
            {
                exit(0);//销毁此线程
                break;
            }
        }
        }
    }
    
    return 0;
}

四、客端1

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 


int main(int argc, const char * argv[]) {
    int client_socket;
    struct sockaddr_in ser_addr;
    char sendbuf[400];
    char recbuf[400];
    int sennum,recnum;
    if((client_socket= socket(AF_INET, SOCK_STREAM, 0))<0)
    {
        perror("socket");
        return -1;
    }
    
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(5901);
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if(connect(client_socket, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0)
    {
        perror("connect");
        return 1;
    }
    
    printf("connect with destination host...\n");
    
    while(1)
    {
        printf("Input your world:>");
        scanf("%s", sendbuf);
        printf("\n");
        
        send(client_socket, sendbuf, strlen(sendbuf), 0);
        
        recnum = recv(client_socket, recbuf, 4000, 0);
        recbuf[recnum] = '\0';
        printf("recv is: %s\n", recbuf);
        if(strcmp(recbuf, "quit") == 0)
            break;
    }
    close(client_socket);
    
    return 0;
}

五、客端2

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 


int main(int argc, const char * argv[]) {
    int client_socket;
    struct sockaddr_in ser_addr;
    char sendbuf[400];
    char recbuf[400];
    int sennum,recnum;
    if((client_socket= socket(AF_INET, SOCK_STREAM, 0))<0)
    {
        perror("socket");
        return -1;
    }
    
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(5901);
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if(connect(client_socket, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0)
    {
        perror("connect");
        return 1;
    }
    
    printf("connect with destination host...\n");
    
    while(1)
    {
        printf("Input your world:>");
        scanf("%s", sendbuf);
        printf("\n");
        
        send(client_socket, sendbuf, strlen(sendbuf), 0);
        
        recnum = recv(client_socket, recbuf, 4000, 0);
        recbuf[recnum] = '\0';
        printf("recv is: %s\n", recbuf);
        if(strcmp(recbuf, "quit") == 0)
            break;
    }
    close(client_socket);
    
    return 0;
}

    总结:《unix网络编程》不仅仅适用于unix系统的网络编程,也讲述了很多通用性的内容,强力安利一波。自己写的这个简单的多并发服务器是个基于tcp的多并发聊天程序。先运行服务器端,然后再运行客户端,服务器端可以与客户12一起聊天,不用等待某一客户的回复。

   代码测试环境是mac 10.13.1,本人不才,若文中有错误,恳请大家积极指正。


  


 

你可能感兴趣的:(基于tcp协议的简单多并发服务器-c语言)