套接字实现TCP服务器

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标示网络通讯中的一个进程,“IP地址+端口号”为socket(套接字)
常用API:

#include<sys/types.h>
#include<sys/socket.h>
int sochet(int domain,int type,int protocol);

socket()打开一个网络通信端口,如果成功,就返回新套接字的文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket调用出错则返回-1。对于IPv4,domain参数指明所使用的协议族,指定为AF_INET,对于TCP协议,type参数指定为SOCK_STREAM,便是面向流的传输协议,protocol参数用来指明所要接受的协议包,指定为0即可。

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const,struct sockaddr* addr,socklen_t addrlen);

服务器调用bind绑定一个固定的网络地址和端口号,bind()成功返回0,失败返回-1。
bind()作用是将参数sockfd和myaddr绑定到一起,使socket这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。struct sockaddr*是一个通用指针类型。

#include<sys/types.h>
#include<sys/socket.h>
int listen(int sockfd,int backlog);

listen()函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr* addr,socklen_t *addrlen);

accept()接受一个套接字中已建立的连接
提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回该套接字的文件文件描述符,失败返回-1

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen);

connect用于客户端建立TCP连接,sockfd标示一个套接字,addr为套接字想要连接的主机地址和端口号,addrlen为缓冲区的长度,连接成功返回0,失败返回-1

代码实现

//tcp_server.c
include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>

static void usage(const char *proc)
{
    printf("Usage:%s [local_ip] [local_port]\n",proc);
}
int startup(const char* _ip,int _port)
{
    int sock=(socket(AF_INET,SOCK_STREAM,0));
    if(sock<0){
        perror("socket");
        exit(2);
    }
    printf("fd:%d\n",sock);
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(_ip);
    //绑定
    if((bind(sock,(struct sockaddr*)&local,sizeof(local)))<0){
        perror("bind");
        exit(3);
    }
    if((listen(sock,10))<0){
        perror("listen");
        exit(4);
    }
    return sock;
}
void * request(void *arg)
{
    int n_sock=(int)arg;
    while(1){
        char buf[1024];
        ssize_t s=read(n_sock,buf,sizeof(buf)-1);
        if(s>0){
            buf[s]=0;
            printf("client:%s\n",buf);
            write(n_sock,buf,strlen(buf));
        }else if(s==0){
            close(n_sock);
            printf("client quit..\n");
            break;
        }
        else{
            printf("read false\n");
            break;
        }

    }
}
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 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));
        //多线程
//      pthread_t id;
//      pthread_create(&id,NULL,request,(void*)new_sock);
//      pthread_detach(id);
        //多进程
//      pid_t id=fork();
//      if(id<0){
//          close(new_sock);
//      }
//      else if(id==0){
//      //  close(listen_sock);
//          if(fork()<0){
//              exit(0);
//          }
//          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){
//                  close(new_sock);
//                  printf("client quit...\n");
//                  break;
//              }
//              else{
//                  perror("read");
//              }
//          }
//      }
//      else{
//          close(new_sock);
//      }
        // 普通版
    char buffer[1024];
    while(1)
    {
        ssize_t s=read(new_sock,buffer,sizeof(buffer)-1);
        if(s>0){
            buffer[s]=0;
            printf("client# %s\n",buffer);
            write(new_sock,buffer,strlen(buffer));
        }else if(s==0){
            printf("client quit!\n");
            break;
        }
        else{
            perror("read");
        }
        }
        close(new_sock);
    }
    close(listen_sock);
    return 0;
}       
//tcp_client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>

static void usage(const char *proc)
{
    printf("Usage:%s[local_ip] [local_port]\n",proc);
}
int main(int argc,char*argv[])
{
    if(argc!=3){
        usage(argv[0]);
        return 1;
    }
    char buf[1024];
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(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(connect(sock,(struct sockaddr*)&server,sizeof(server))<0){
        perror("connect");
        exit(3);
    }
    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));
            int _s=read(sock,buf,sizeof(buf)-1);
            if(_s>0){
                buf[_s]=0;
                printf("server echo# %s\n",buf);
            }
        }
    }

    return 0;
}

普通版本地连接运行结果:

这里写图片描述

普通版本无法实现多个客户端同时连接服务器,向服务器发送消息,只有当一个客户端断开时其他客户端才能进行连接输入消息

这里写图片描述

多线程版本地连接运行结果:
这里写图片描述

多进程版本本地连接运行结果:

这里写图片描述

PS:为了说明现象,图截到一块了,可以缩放一下网页进行查看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值