linux:套接字编程 (单连接/多进程版本/多线程版本) TCP代码的实现 netstat -anp|grep xxx 命令

一、TCP单连接版本

1.实现服务器与客户端之间的通信,客户端给服务器发送消息,服务器的作用是把客户端发给自己的消息再给客户端发送回去

服务器端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)

void Usage(){
    printf("Usage: ./server [ip] [port]\n");
    return;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    //1.创建socket
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=inet_addr(argv[1]);
    local.sin_port=htons(atoi(argv[2]));
    //2.将文件描述符(sockfd)和服务器的地址(ip+port)关联到一起
    int ret=bind(sockfd,(sockaddr*)&local,sizeof(local));
    if(ret<0)
        ERR_EXIT("bind");
    //3.监听,是服务器处于一种被动可以接受客户端请求的状态
    if(listen(sockfd,5)<0)
        ERR_EXIT("listen");
    printf("bind and listen success,please accept......\n");
    //4.循环的接受客户端的一个连接
    while(1){
        sockaddr_in client;
        socklen_t len=sizeof(client);
        int client_fd=accept(sockfd,(sockaddr*)&client,&len);
        if(client_fd<0){
            close(sockfd);
            break;
        }
        printf("accept success!\n");
        //5.循环的接收客户端发送消息,和给客户端发送消息
        while(1){
            char buf[1024]={0};
            //读取客户端发送的消息
            ssize_t read_size =read(client_fd,buf,sizeof(buf)-1);
            if(read_size<0){
                ERR_EXIT("read");
                break;
            }
            if(read_size==0){
                printf("client [%s:%d] quit!\n",\
                        inet_ntoa(client.sin_addr),\
                        ntohs(client.sin_port));
                break;
            }
            buf[read_size]=0;
            //输出客户端发送的消息
            printf("client [%s:%d] say:>  %s\n",
                    inet_ntoa(client.sin_addr),\
                    ntohs(client.sin_port),buf);
            //给客户端回复消息,从键盘读入要发送的消息
            printf("please enter:>  ");
            fflush(stdout);
            memset(buf,0,sizeof(buf));
            ssize_t s=read(0,buf,sizeof(buf)-1);
            if(s<0){
             ERR_EXIT("read");
             break;
             }
             printf("please wait......\n");
            write(client_fd,buf,strlen(buf));
        }
        close(client_fd);
    }
    close(sockfd);
    return 0;
}

客户机端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void Usage(){
    printf("Usage: ./client [ip] [port]\n");
    return ;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    //1.创建socket
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));
    //2.给服务器发送连接请求
    int ret=connect(sockfd,(sockaddr*)&server,sizeof(server));
    if(ret<0)
        ERR_EXIT("connect");
    printf("connect success!\n");
    //3.循环的给服务器发送消息,和接收服务器发来的消息
    socklen_t len=sizeof(server);
    while(1){
        char buf[1024]={0};
        //从键盘读要发给服务器的消息
        printf("please enter:>  ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1);
        printf("please wait......\n");
        if(s>0){
            buf[s-1]=0;
            //把读到的消息发给服务器
            ssize_t write_size=write(sockfd,buf,strlen(buf));
            //输出服务器发的消息
            memset(buf,0,sizeof(buf));
            ssize_t read_size=read(sockfd,buf,sizeof(buf)-1);
            if(read_size>0)
            {
                buf[read_size]=0;
                printf("server echo:>   %s\n",buf);
            }
        }
    }
    close(sockfd);
    return 0;
}

运行结果:
这里写图片描述

上面的程序有一个缺陷,那就是不能接受多个客户端的请求连接,因为服务器执行完accept之后,服务器就一直在while循环尝试read了,没有在继续调用accept了,从而导致服务器不能接受别的连接了

二、TCP的多个连接版本

为了解决上面所说的问题,那我们采用多个执行流把accept和read分开不就好了吗,一个执行流负责accept,另一个执行流负责read,这样就可以同时处理多个连接了,因为这两个执行流是并发执行的,负责执行accept的执行流它接受完一个客户端的连接之后,此时也可以去接受别的客户端的请求,而那个负责执行读写操作的执行流,不停的对多个客户端进行读写

2.使用多进程实现TCP通信

父进程负责不停地接受客户端的连接,即就是服务器执行accept,使用父进程在创建一个子进程,子进程负责执行读写操作

ps:因为子进程在退出时,会发送SIGCHLD信号,父进程需要把子进程的资源回收掉,否则会导致僵尸进程,现在回收子进程的资源有三种方式:

(1)使用wait函数,等待子进程执行完,wait是阻塞式的等待,父进程在等待子进程的时候,什么事也做不了,只能一直等待,那此时使用wait函数的话,还是实现不了多个客户端的连接,因为父进程执行accept接受一个请求之后,子进程在while循环中,一直在尝试读写,就不会再调用到accept函数了,此种方法不能处理多个客户端的连接请求
(2)使用waitpid函数,设置选项WNOHANG,此时的话waitpid函数就是非阻塞式的等待了,但是会出现子进程还没执行完,父进程就已经执行结束了,还是会出现僵尸进程,如果采用轮询的方式,不停的去查看子进程的存在状态,一会看一下子进程有没有结束,从而回收子进程的资源,这样就会导致父进程可能不能及时的accept了,一心两用,不能保证质量哦!
(3)捕捉信号:捕捉子进程结束后发送给父进程的SIGCHLD信号,把此信号给忽略掉,就不会出现僵尸进程了,也可以实现把accept和read分离开

服务器端

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<signal.h>
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
void Usage(){
    printf("Usage: ./server [ip] [port]\n");
    return;
}
void ProcessConnect(int client_fd,sockaddr_in client)
{
    int pid=fork();
    if(pid<0)
        ERR_EXIT("fork");
    else if(pid>0){
        close(client_fd);
        return;
    }
    else{
        printf("\n");
        //子进程进行循环的读写
        while(1){
            char buf[1024]={0};
            ssize_t read_size=read(client_fd,buf,sizeof(buf)-1);
            if(read_size<0){
                perror("read");
                close(client_fd);
                break;
            }
            if(read_size==0){
                printf("client[%s:%d] quit!\n",inet_ntoa(client.sin_addr),\
                        ntohs(client.sin_port));
                close(client_fd);
                exit(0);
            }
            buf[read_size]=0;
            printf("client[%s:%d]say:>  %s\n",inet_ntoa(client.sin_addr),\
                    ntohs(client.sin_port),buf);
            printf("please enter:>  ");
            fflush(stdout);
            ssize_t s=read(0,buf,sizeof(buf));
            if(s<0)
                ERR_EXIT("read");
            if(s>0){
                buf[s-1]=0;
                write(client_fd,buf,strlen(buf));
            }
            printf("please wait......\n");
        }
    }
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    signal(SIGCHLD,SIG_IGN);
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=inet_addr(argv[1]);
    local.sin_port=htons(atoi(argv[2]));
    int ret=bind(sockfd,(sockaddr*)&local,sizeof(local));
    if(ret<0)
        ERR_EXIT("bind");
    if(listen(sockfd,5)<0)
        ERR_EXIT("listen");
    printf("bind and listen success,please accept......\n");
    sockaddr_in client;
    socklen_t len=sizeof(client);
    while(1){
        int client_fd=accept(sockfd,(sockaddr*)&client,&len);
        if(client_fd<0){
            perror("accept");
            break;//跳出if,开始下一次的while
        }
        printf("accept succcess!\n");
        ProcessConnect(client_fd,client);
    }
    close(sockfd);
    return 0;
}

客户机端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void Usage(){
    printf("Usage: ./client [ip] [port]\n");
    return ;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    //1.创建socket
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));
    //2.给服务器发送连接请求
    int ret=connect(sockfd,(sockaddr*)&server,sizeof(server));
    if(ret<0)
        ERR_EXIT("connect");
    printf("connect success!\n");
    //3.循环的给服务器发送消息,和接收服务器发来的消息
    socklen_t len=sizeof(server);
    while(1){
        char buf[1024]={0};
        //从键盘读要发给服务器的消息
        printf("please enter:>  ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1);
        printf("please wait......\n");
        if(s>0){
            buf[s-1]=0;
            //把读到的消息发给服务器
            ssize_t write_size=write(sockfd,buf,strlen(buf));
            //输出服务器发的消息
            memset(buf,0,sizeof(buf));
            ssize_t read_size=read(sockfd,buf,sizeof(buf)-1);
            if(read_size>0)
            {
                buf[read_size]=0;
                printf("server say:>   %s\n",buf);
            }
        }
    }
    close(sockfd);
    return 0;
}

运行结果:
这里写图片描述
ps:服务器的IP地址设为0,表示这服务器可以关联任一个地址
127.0.0.1是环回地址,是留给用户自己使用的,故客户端可以使用该地址

3.实现多线程版本的TCP通信,主线程负责accept(接受客户端发来的连接请求),新创建的线程负责循环的执行读写操作

服务器端

扫描二维码关注公众号,回复: 998246 查看本文章
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
//封装消息
typedef struct Arg
{ 
    int fd;
    sockaddr_in addr;
}Arg;
void*ThreadEntry(void*ptr)
{
    Arg*arg=(Arg*)ptr;
    while(1){
        char buf[1024]={0};
        ssize_t read_size=read(arg->fd,buf,sizeof(buf)-1);
        if(read_size<0){
            close(arg->fd);
            perror("read");
            break;
        }
        if(read_size==0){
            printf("client [%s:%d] quit!\n",inet_ntoa(arg->addr.sin_addr),\
                    ntohs(arg->addr.sin_port));
            close(arg->fd);
            free(arg);
            return NULL;

        }
        printf("client [%s:%d]:>  %s\n",inet_ntoa(arg->addr.sin_addr),\
                ntohs(arg->addr.sin_port),buf);
        memset(buf,0,sizeof(buf));
        printf("please enter:>  ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1);
        if(s>0){
            buf[s-1]=0;
            write(arg->fd,buf,strlen(buf));
        }
        printf("please wait......\n");
    }
    return NULL;
}
void ProcessConnect(int client_fd,sockaddr_in client)
{
    Arg* arg=(Arg*)malloc(sizeof(Arg));
    arg->fd=client_fd;
    arg->addr=client;
    pthread_t tid;
    pthread_create(&tid,NULL,ThreadEntry,(void*)arg);
    pthread_detach(tid);
}
void Usage(){
    printf("Usage: ./server [ip] [port]\n");
    return;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=inet_addr(argv[1]);
    local.sin_port=htons(atoi(argv[2]));
    int ret=bind(sockfd,(sockaddr*)&local,sizeof(local));
    if(ret<0)
        ERR_EXIT("bind");
    if(listen(sockfd,5)<0)
        ERR_EXIT("listen");
    printf("bind and listen success,please accept......\n");
    sockaddr_in client;
    socklen_t len=sizeof(client);
    while(1){
        int client_fd=accept(sockfd,(sockaddr*)&client,&len);
        if(client_fd<0)
        {
            perror("accept");
            break;
        }
        printf("accept success!\n");
        ProcessConnect(client_fd,client);
    }
    close(sockfd);
    return 0;
}

客户机端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void Usage(){
    printf("Usage: ./client [ip] [port]\n");
    return ;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    //1.创建socket
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));
    //2.给服务器发送连接请求
    int ret=connect(sockfd,(sockaddr*)&server,sizeof(server));
    if(ret<0)
        ERR_EXIT("connect");
    printf("connect success!\n");
    //3.循环的给服务器发送消息,和接收服务器发来的消息
    socklen_t len=sizeof(server);
    while(1){
        char buf[1024]={0};
        //从键盘读要发给服务器的消息
        printf("please enter:>  ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1);
        printf("please wait......\n");
        if(s>0){
            buf[s-1]=0;
            //把读到的消息发给服务器
            ssize_t write_size=write(sockfd,buf,strlen(buf));
            //输出服务器发的消息
            memset(buf,0,sizeof(buf));
            ssize_t read_size=read(sockfd,buf,sizeof(buf)-1);
            if(read_size>0)
            {
                buf[read_size]=0;
                printf("server say:>   %s\n",buf);
            }
        }
    }
    close(sockfd);
    return 0;
}

运行结果:
这里写图片描述

相关命令

1.netsat -anp|grep +服务器的端口号//查看该服务器的信息
2.netstat -anp|grep -n2//显示查询出来的信息的格式

这里写图片描述

Local Address    本地IP和端口
Foreign Address  对端IP和端口
State:是当前进程的状态,LISTEN表示此进程处于监听状态,ESTABLISHED表示此进程已经建立连接
Recv-Q和Send-Q表示流量
0.0.0.0:*//是任何IP和端口,此种使用方法表示对任何IP和端口,都可以处理该IP和端口定义的套接字的通信

netstat -nt|grep 服务器的端口号//显示服务器中监听队列中的内容

这里写图片描述

相关函数的用法请参考博客:
https://blog.csdn.net/dangzhangjing97/article/details/80280770
IP地址和端口请参考博客:
https://blog.csdn.net/dangzhangjing97/article/details/80280254
关于网络字节序与主机字节序之间转换的相关函数:
https://blog.csdn.net/dangzhangjing97/article/details/80303625

三.使用TCP实现一个加法器

服务器负责计算客户端发送的数据,客户端再把结果输出到屏幕上

#include "proto.h"
void Usage(){
    printf("Usage:./server [ip] [port]\n");
    return;
}
int main(int argc,char*argv[])
{ 
    if(argc!=3){
        Usage();
        return 1;
    }
    //创建一个socket文件描述符
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    //if(sockfd<0)
    //  ERR_EXIT("socket");
    sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=inet_addr(argv[1]);
    local.sin_port=htons(atoi(argv[2]));
    Response response;
    Request request;
    //将文件描述符和服务器的地址(ip+port)关联到一起
    if(bind(sockfd,(sockaddr*)&local,sizeof(local))<0)
        ERR_EXIT("bind");
    //将服务器转换成被动状态,以便于接受客户端发送的请求连接
    if(listen(sockfd,5)<0)
        ERR_EXIT("listen");
    printf("bind and listen success,wait accept......\n");
     sockaddr_in client;
    socklen_t len=sizeof(client);
    //循环的接受客户端的连接请求,完成客户端的需求
    while(1){
        int client_fd=accept(sockfd,(sockaddr*)&client,&len);
        if(client_fd<0){
            close(client_fd);
            break;//重新开始下一次的接收来自客户端的连接
        }
        //此时已经和客户端建立连接,循环的与客户端发送消息
        while(1){
            //读客户端需要求和的数字
            ssize_t read_size=read(client_fd,&request,sizeof(request));
            if(read_size>0){
                response.sum=request.a+request.b;
            }
            //把计算好的结果发给客户端
            write(client_fd,&response,sizeof(response));
        }
    }
    close(sockfd);
    return 0;
}

客户机端

#include"proto.h"
void Usage(){
    printf("Usage ./client [ip] [port]\n");
    return;
}
int main(int argc,char*argv[])
{
    if(3!=argc){
        Usage();
        return 1;
    }
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
        ERR_EXIT("socket");
     sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));
    Request request;
    Response response;
    socklen_t len=sizeof(server);
    if(connect(sockfd,(sockaddr*)&server,len)<0)
        ERR_EXIT("connect");
    while(1){
        char buf[1024]={0};
        printf("请输入需要求和的两个数:>  ");
        fflush(stdout);
        scanf("%d %d",&request.a,&request.b);
        //把需要求和的两个数发给服务器(服务器是计算结果的)
        write(sockfd,&request,sizeof(request));
        //读取服务器计算的结果
        ssize_t s=read(sockfd,&response,sizeof(response));
        if(s>0){
            printf("结果为:> %d\n",response.sum);
        }
    }
    close(sockfd);
    return 0;
}

运行结果:
这里写图片描述
结果解析:服务器负责计算结果,并把计算的结果发给客户机,客户机把自己想要计算的数据发给服务器就好,然后接收服务器的计算结果就OK,最后把计算的结果输出到屏幕上

猜你喜欢

转载自blog.csdn.net/dangzhangjing97/article/details/80316680
今日推荐