Bootstrap

linux下 socket tcp Server c语言编写(分别实现单进程,多进程,多线程)

文章系转载,便于整理和归纳,原文地址:https://blog.csdn.net/jelly_gemini/article/details/76736335

TCP 迭代服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。TCP 迭代服务器一次只能处理一个客户端的请求,只有在这个客户的所有请求满足后,服务器才可以继续后面的请求。如果有一个客户端占住服务器不放时,其它的客户机都不能工作了,因此,TCP 服务器一般很少用迭代服务器模型的。

tcp服务器端框架

  1. 创建tcp套接字

  2. 绑定套接字

  3. 监听套接字

  4. 调用accept()阻塞等待

  5. 处理客户端的请求

  6. 关闭连接套接字

  7. 关闭监听套接字

tcp客户端框架

  1. 创建tcp套接字

  2. 调用connect()连接服务器

  3. 处理服务器端返回的信息

由于客户端不需要固定的端口号,因此不必调⽤bind(),客户端的端口号由内核⾃动分配。注意, 客户端不是不允许调⽤bind(),只是没有必要调⽤bind()固定⼀个端口号,服务器也不是必须调⽤bind(),但如果服务器不调⽤bind(),内核会⾃动给服务器分配监听端口,每次启动服务器时端口号都不⼀样,客户端要连接服务器就会遇到⿇烦。

单进程实现
server.c

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{                                                                                                          
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        printf("socket()\n");
    }
    struct sockaddr_in server_socket;
    struct sockaddr_in socket;
    bzero(&server_socket,sizeof(server_socket));
    server_socket.sin_family=AF_INET;
    server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
    server_socket.sin_port=htons(_PORT_);
    if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
    {
        printf("bind()\n");
        close(sock);
        return 1;
    }
    if(listen(sock,_BACKLOG_)<0)
    {
        printf("listen()\n");
        close(sock);
        return 2;
    }
    printf("success\n");  
     for(;;)
    {
        socklen_t len=0;
        int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
        if(client_sock<0)
        {
            printf("accept()\n");
            return 3;
        }
        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip,'\0',sizeof(buf_ip));

        inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));

        printf("get connect\n");

        while(1)
        {
            char buf[1024];
            memset(buf,'\0',sizeof(buf));
            read(client_sock,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';
            if(strncasecmp(buf,"quit",4)==0)
            {
                printf("quit\n");
                break;
            }
            write(client_sock,buf,strlen(buf)+1);
            printf("wait...\n");
        }
        close(client_sock);
    }
    close(sock);
    return 0;
}                      

多进程实现
怎么将单进程的代码改为多进程的代码呢?
调用fork()函数,创建子进程,将所有的客户端的请求处理的内容都放在子进程中处理。
在 Linux 环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时,服务器能及时处理客户,特别是在客户服务器交互系统中。对于一个 TCP 服务器,客户与服务器的连接可能并不马上关闭,可能会等到客户提交某些数据后再关闭,这段时间服务器端的进程会阻塞,所以这时操作系统可能调度其它客户服务进程,这比起循环服务器大大提高了服务性能。
server.c

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>                                                                                      
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        printf("socket()\n");
    }
    struct sockaddr_in server_socket;
    struct sockaddr_in socket;
    bzero(&server_socket,sizeof(server_socket));
    server_socket.sin_family=AF_INET;
 server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
    server_socket.sin_port=htons(_PORT_);
    if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
    {
        printf("bind()\n");
        close(sock);
        return 1;
    }
    if(listen(sock,_BACKLOG_)<0)
    {
        printf("listen()\n");
        close(sock);
        return 2;
    }
    printf("success\n");
    for(;;)
    {
        socklen_t len=0;
        int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
        if(client_sock<0)
        {        
             printf("accept()\n");
            return 3;
        }
        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip,'\0',sizeof(buf_ip));

        inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));

        printf("get connect\n");
        pid_t fd=fork();
        if(fd<0)
            printf("fork()\n");
        if(fd==0)
        {
          close(sock);//关闭监听套接字
                     printf("port=%d,ip=%s\n",ntohs(socket.sin_port),buf_ip); 
           while(1)
           {
               char buf[1024];
               memset(buf,'\0',sizeof(buf));
               read(client_sock,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';
               if(strncasecmp(buf,"quit",4)==0)
               {
                   printf("quit\n");
                   break;
               }
               write(client_sock,buf,strlen(buf)+1);
               printf("wait...\n");
           }
    close(fd);  
    }
        else if(fd>0)
        {
            close(fd);          
              }
    }
    close(sock);
    return 0;
}

多线程实现
多线程和多进程的处理方式类似,都是创建一个新的线程,客户端有请求时,用新创建的线程处理。

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
void *fun(void* arg)
{
    int client_sock = (int)arg;
        while(1)
        {  
            char buf[1024];
            memset(buf,'\0',sizeof(buf));
            read(client_sock,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';
            if(strncasecmp(buf,"quit",4)==0)
            {
                printf("quit\n");
                break;
            }
            write(client_sock,buf,strlen(buf)+1);
            printf("wait...\n");
        }
    close(client_sock);
}
int main()
{
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        printf("socket()\n");
    }
    struct sockaddr_in server_socket;
    struct sockaddr_in socket;
     pthread_t thread_id;  
    bzero(&server_socket,sizeof(server_socket));
    server_socket.sin_family=AF_INET;
    server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
    server_socket.sin_port=htons(_PORT_);
    if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
    {
        printf("bind()\n");
        close(sock);
        return 1;
    }
    if(listen(sock,_BACKLOG_)<0)
    {
        printf("listen()\n");
        close(sock);
        return 2;
    }
    printf("success\n");
    for(;;)
    {
        socklen_t len=0;
        int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
        if(client_sock<0)
        {
            printf("accept()\n");
            return 3;
        }
        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip,'\0',sizeof(buf_ip));     
        inet_ntop(AF_INET,&socket.sin_add,buf_ip,sizeof(buf_ip));
        printf("get connect,ip is%s\n",buf_ip);
        printf("port=%d\n",ntohs(socket.sin_port));  
        pthread_create(&thread_id, NULL, (void *)fun, (void *)client_sock);  
        pthread_detach(thread_id); 
    }
    close(sock);
    return 0;
}

不管是哪一种的服务器,客户端都是一样的
client.c

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#define SERVER_PORT 9999
int main(int argc,char* argv[])
{
    if(argc!=2)                                                                                            
    {   
        printf("Usage:client IP\n");
        return 1;
    }   
    char *str=argv[1];
    char buf[1024];
    memset(buf,'\0',sizeof(buf));

struct sockaddr_in server_sock;
    int sock = socket(AF_INET,SOCK_STREAM,0);
    bzero(&server_sock,sizeof(server_sock));
    server_sock.sin_family=AF_INET;
    inet_pton(AF_INET,str,&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()\n");
        return 1;
    }
    printf("connect success\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("wait..\n");
        read(sock,buf,sizeof(buf));
        printf("server:$ %s\n",buf);
    }
    close(sock);
    return 0;
}
;