多进程多线程TCP服务器

来源:互联网 发布:java 文件base64编码 编辑:程序博客网 时间:2024/06/07 00:25

在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 8080static 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 8080static 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;}
原创粉丝点击