服务器/客户端简单实现(初学网络编程)

来源:互联网 发布:网络电视怎么连接wifi 编辑:程序博客网 时间:2024/05/29 18:26

声明,本文代码摘自linux内核
服务器端的设计模式
主要有:套接字初始化,套接字与端口的绑定,设置服务器的侦听链接,接受和发送数据并进行数据处理及处理完毕关闭套接字。
客户端的设计模式:
主要分为套接字初始化,连接服务器,读写网络数据,并进行后续释放资源处理。
这应该是网络编程最基本的框架。刚开始学习,只能从基本的开始。
linux中,每一个应用层函数都会和内核相关的系统调用进行交互:
举个例子:
初始化套接字int sock = socket(AF_INET, SOCK_STREAM, 0); 它会调用系统调用函数sys_socket(AF_INET, SOCK_STREAM, 0);此函数分两个部分,第一生成socket结构,第二与文件描述符绑定,将绑定的描述符传递给应用层。下面贴出源码可以看看:

asmlinkage long sys_socket(int family, int type, int protocol){    int retval;    struct socket *sock;    retval = sock_create(family, type, protocol, &sock);    if (retval < 0)        goto out;    retval = sock_map_fd(sock);    if (retval < 0)        goto out_release;out:    /* It may be already another descriptor 8) Not kernel problem. */    return retval;out_release:    sock_release(sock);    return retval;}

sock_create()根据用户的domain指定的协议族,创建一个内核socket结构绑定到当前进程上,其中type和用户空间的设置相同。sock_map_fd()函数将socket结构与文件描述符列表中的某个文件绑定,之后操作可以查找文件描述符列表来对应内核的socket结构。

其他的过程也都是通过系统调用内核函数完成的。
bind()函数对套接字进行地址和端口的绑定,才能进行数据的传送。
应用层与内核函数之间的关系:传递相应的参数给内核。
相应的内核代码:

asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen){    struct socket *sock;    char address[MAX_SOCK_ADDR];    int err;    if((sock = sockfd_lookup(fd,&err))!=NULL)    {        if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) {            err = security_socket_bind(sock, (struct sockaddr *)address, addrlen);            if (err) {                sockfd_put(sock);                return err;            }            err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen);        }        sockfd_put(sock);    }               return err;}

listen()函数,listen 需要维护一个等待队列。故要设置队列的长度。
相应的内核代码:

asmlinkage long sys_listen(int fd, int backlog){    struct socket *sock;    int err;    if ((sock = sockfd_lookup(fd, &err)) != NULL) {        if ((unsigned) backlog > sysctl_somaxconn)            backlog = sysctl_somaxconn;        err = security_socket_listen(sock, backlog);        if (err) {            sockfd_put(sock);            return err;        }        err=sock->ops->listen(sock, backlog);        sockfd_put(sock);    }    return err;}

accept()函数:当服务器成功处理客户端请求连接时,会有两个文件描述符。老的文件描述符表示正在监听的socket,新产生的文件描述符表示客户端的连接。send()和recv()都是通过新的文件描述符来进行数据收发的。
accept内核层代码比较长

asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen){    struct socket *sock, *newsock;    int err, len;    char address[MAX_SOCK_ADDR];    sock = sockfd_lookup(fd, &err);    if (!sock)        goto out;    err = -ENFILE;    if (!(newsock = sock_alloc()))         goto out_put;    newsock->type = sock->type;    newsock->ops = sock->ops;    err = security_socket_accept(sock, newsock);    if (err)        goto out_release;    /*     * We don't need try_module_get here, as the listening socket (sock)     * has the protocol module (sock->ops->owner) held.     */    __module_get(newsock->ops->owner);    err = sock->ops->accept(sock, newsock, sock->file->f_flags);    if (err < 0)        goto out_release;    if (upeer_sockaddr) {        if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {            err = -ECONNABORTED;            goto out_release;        }        err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);        if (err < 0)            goto out_release;    }    /* File flags are not inherited via accept() unlike another OSes. */    if ((err = sock_map_fd(newsock)) < 0)        goto out_release;    security_socket_post_accept(sock, newsock);out_put:    sockfd_put(sock);out:    return err;out_release:    sock_release(newsock);    goto out_put;}

accept内部会新创建一个socket,并进行相应的设置,产生对应的文件描述符。
客户端connect()函数与内核函数之间的关系:

asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen){    struct socket *sock;    char address[MAX_SOCK_ADDR];    int err;    sock = sockfd_lookup(fd, &err);    if (!sock)        goto out;    err = move_addr_to_kernel(uservaddr, addrlen, address);    if (err < 0)        goto out_put;    err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);    if (err)        goto out_put;    err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen,                 sock->file->f_flags);out_put:    sockfd_put(sock);out:    return err;}

学习网络基础编程,结合内核代码实现,更好的理解数据传输过程和内核底层都进行了那些操作。
以下为典型的c/s网络编程模型:

/* * serverDemo.c * *  Created on: 2016年9月19日 *      Author: huwang */#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<string.h>#include<unistd.h>#define PORT 8888#define BACKLOG 2void process_conn_server( int s);int main(int argc, char **argv){    int ss, sc;    struct sockaddr_in server_addr;    struct sockaddr_in client_addr;    int err;    pid_t pid;    ss = socket(AF_INET, SOCK_STREAM, 0);    if(ss < 0){        printf("socket error!\n");        return -1;    }    bzero(&server_addr, sizeof(server_addr));    server_addr.sin_family = AF_INET;    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    server_addr.sin_port = htons(PORT);    err = bind(ss, (struct sockaddr * )&server_addr, sizeof(server_addr));    if(err < 0){        printf("bind error!\n");        return -1;    }    err = listen(ss, BACKLOG);    if(err < 0){        printf("listen error!\n");        return -1;    }    for( ; ; ){        socklen_t addrlen = sizeof(struct sockaddr);        sc = accept(ss, (struct sockaddr *)&client_addr, &addrlen);        if(sc < 0){            continue;        }        pid = fork();        if(pid == 0){            process_conn_server(sc);            close(ss);        }else{            close(sc);        }    }}void process_conn_server( int s){    ssize_t size = 0;    char buffer[1024];    for( ; ; ){        size = read(s, buffer, 1024);        if(size == 0){            return;        }        sprintf(buffer, "%zd bytes altogeter\n", size);        write(s, buffer, strlen(buffer) + 1);    }}
/* * clientDemo.c * *  Created on: 2016年9月19日 *      Author: huwang */#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<string.h>#include<sys/socket.h>#include<arpa/inet.h>#include<signal.h>#include<unistd.h>#define PORT 8888int s;void process_conn_client(int s);static void sig_handle(int sign);int main(int argc, char **argv) {    struct sockaddr_in server_addr;    signal(SIGPIPE, sig_handle);    s = socket(AF_INET, SOCK_STREAM, 0);    if(s < 0 ){        printf("socket error!\n");        return -1;    }    bzero(&server_addr, sizeof(server_addr));    server_addr.sin_family = AF_INET;    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    server_addr.sin_port = htons(PORT);    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);    connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));    process_conn_client(s);    close(s);    return 0;}void process_conn_client(int s){    ssize_t size = 0;    char buffer[1024];    for(; ;){        size = read(0, buffer, 1024);        if(size > 0){            write(s, buffer, size);            size = read(s, buffer, 1024);            write(1, buffer, size);        }    }}static void sig_handle(int sign){    if(sign == SIGPIPE)    {        printf("Catch a signpipe signal !\n");    }else if(sign == SIGINT){        printf("Catch a signint signal !\n");    }    close(s);    return;}

我想在这个基础上可以进行拓展。

0 0
原创粉丝点击