基于TCP的客户服务器模式的三种通信

在以下的客户端/服务器程序实例中,TCP_server.c的作用是接受client请求,并与client进行简单的数据通信,整体为一个阻塞式的网络聊天工具。

首先我们需要了解几个socket API:

  • int socket(int domain, int type, int protocol);
    domian指协议族/域,IPV4采用AF_INET;
    type是套接口类型,对于TCP采用SOCK_STREAM;
    protocol一般取为0.成功返回一个小的非负整数值,与文件描述符类似

  • int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
    当 socket函数返回一个文件描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(IPV4和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。成功返回0,失败返回-1。

    sockfd是socket函数返回的描述符;
    addr指定了想要绑定的IP和端口号,均要使用网络字节序-即大端模式;
    addrlen是前面struct sockaddr(与sockaddr_in等价)的长度。
    struct sockaddr_in {
    short int sin_family; /* 地址族 */
    unsigned short int sin_port; /* 端口号 */
    struct in_addr sin_addr; /* Internet地址 */
    unsigned char sin_zero[8]; /* 与struct sockaddr一样的长度 */
    };

  • int listen(int sockfd, int backlog);
    listen声明sockfd处于监听状态,并且最多允许backlog个客户处于连接状态,该参数至少为1,一般设置为5;成功返回0,失败返回-1

  • int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
    accept有服务器调用,第二个参数为输出型参数,第三个为输入输出型。

  • int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
    通过connect函数建立于TCP服务器的连接,实际是发起三次握手过程,仅在连接成功或失败后返回。参数sockfd是本地描述符,addr为服务器地址,addrlen是socket地址长度。

  • int addr_t inet_addr(const char *cp);
    该函数是把字符串形式的点分十进制转化成32bit的整数

  • char *inet_ntoa(struct in_addr in);
    该函数将32bit的整数转化成点分十进制

1、单进程TCP服务器
tcp_server.c

 #include<stdio.h>
 #include<sys/types.h>
 #include<sys/socket.h>
 #include<stdlib.h>
 #include<netinet/in.h>
 #include<string.h>
//./tcp_server[local_ip][local_port]
static 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 listen_sock = socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock < 0)
    {
        perror("socket");
        exit(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(bind(listen_sock,(struct sockaddr*)&server,sizeof(server))<0)
    {
        perror("bind");
        exit(3);
    }

    if(listen(listen_sock,5)<0)
    {
        perror("listen");
        exit(4);
    }
    printf("server bind and listen success, waiting accept...\n");
    while(1)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
        if(new_sock < 0)
        {
            perror("accept");
            continue;
        }
        printf("get a new
        client...,%s:%d\n",inet_ntoa(client.sin_addr),
        ntohs(client.sin_port));
        char buf[1024];
        while(1)
        {
            ssize_t r = read(new_sock,buf,sizeof(buf)-1);
            if(r > 0)
            {
                buf[r] = 0;
                printf("client:%s\n",buf);
                write(new_sock,buf,strlen(buf));
            }
            else if(r == 0){
                close(new_sock);
                printf("client quit...\n");
                break;
            }
            else
            {
                perror("read");
                close(new_sock);
                break;
            }
        }
    }
    return 0;
}

#include

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
// ./client server_ip server_port
int main(int argc,char* argv[])
{
       if(argc != 3)
    {   
       printf("please input:%s server_ip server_port...\n",argv[0]);    
       return 1;
    }   
     int sock = socket(AF_INET,SOCK_STREAM,0);
     if(sock < 0)
    {   
         perror("sock");
         exit(2);
    }   
     struct sockaddr_in client;
     client.sin_family = AF_INET;
     client.sin_port = htons(atoi(argv[2]));
          client.sin_addr.s_addr = inet_addr(argv[1]);

    if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
            perror("connect");
            exit(3);
    }

    printf("connect server success...\n");
    char buf[10240];
     while(1)
   {
      printf("please Enter#: ");
      fflush(stdout);
      ssize_t r = read(0,buf,sizeof(buf)-1);
      if(r > 0)
     {
         buf[r-1] = 0;
         write(sock,buf,strlen(buf));
         r = read(sock,buf,sizeof(buf)-1);
         if(r > 0)
        {
           buf[r] = 0;
           printf("server echo#: %s\n",buf);
        }
     }
    }
     return 0;
}                                                                                                         

这里写图片描述
这里写图片描述

2、多进程版本
将tcp_server.c部分内容修改:
tcp_forkserver.c:

 53         printf("get a new client...,%s:%d\n",inet_ntoa(client.sin_addr),
 54         ntohs(client.sin_port));
 55 
 56         pid_t id=fork();
 57         if(id<0)
 58         {
 59             close(new_sock);
 60         }
 61         else if(id==0)
 62         {
 63             close(listen_sock);
 64             if(fork()>0)
 65             {
 66                 exit(0);
 67             }
 68         while(1)
 69         {
 70             char buf[1024];
 71             ssize_t r = read(new_sock,buf,sizeof(buf)-1);
 72             if(r > 0)
 73             {
 74                 buf[r] = 0;
 75                 printf("client:%s\n",buf);
 76                 write(new_sock,buf,strlen(buf));
 77             }
 78             else if(r == 0){
 79                 close(new_sock);
 80                 printf("client quit...\n");
 81                 break;
 82             }
 83             else
 84             {
 85                 perror("read");
 86                 close(new_sock);
 87                 break;
 88             }
 89          }
 90         close(new_sock);
 91        }
 92        else{
 93            close(new_sock);
 94        }
 95     }
 96 
 97     return 0;
 98 }

这里写图片描述
这里写图片描述

3、多线程版本:
将原来的tcp_server.c 中内容部分修改到
tcp_threadserver.c:

1.添加handler函数
void* handler(void* arg)
{ 
    int new_sock=(int)arg;
        while(1)
        {
            char buf[1024];
            ssize_t r = read(new_sock,buf,sizeof(buf)-1);
            if(r > 0)
            {
                buf[r] = 0;
                printf("client:%s\n",buf);
                write(new_sock,buf,strlen(buf));
            }
            else if(r == 0){
                close(new_sock);
                printf("client quit...\n");
                break;
            }
            else
            {
                perror("read");
                close(new_sock);
                break;
            }
        }

}
2.修改main函数中以下部分:
printf("get a new client...,%s:%d\n",inet_ntoa(client.sin_addr),
        ntohs(client.sin_port));

        pthread_t id;
        pthread_create(&id,NULL,handler,(void*)new_sock);
        pthread_detach(id);
    }

    return 0;
}

这里写图片描述
这里写图片描述

在以上程序中我们先后启动服务器端和客户端程序,然后按CTRL+C终止掉服务器端程序,这时再运行服务器端程序,就会出现绑定的错误。这是因为,虽然服务器端的应用程序终止了,但是TCP协议层的连接并没有完全断开,服务器是主动断开连接的一方,所以在TIME_WAIT期间仍然不能再次监听同样的服务器端口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值