服务器/客户端简单实现(初学网络编程)
来源:互联网 发布:网络电视怎么连接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;}
我想在这个基础上可以进行拓展。
- 服务器/客户端简单实现(初学网络编程)
- 网络编程_简单客户端和服务器实现
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- java网络编程之简单客户端服务器
- 简单的网络编程--服务器,客户端呼应
- boost:asio网络库初学之echo服务器客户端实现
- linux网络编程--服务器客户端(TCP实现)
- socket编程(一),实现服务器与客户端简单通信
- Python 网络编程---简单的服务器与客户端实现---阻塞式编写
- 网络编程:使用Socket实现简单的服务器和客户端的通信
- 网络编程,服务器客户端
- UNIX网络编程练习-简单的时间服务器和客户端
- 网络编程基础(服务器,客户端)
- Linux网络编程-简单的客户端和服务器通讯程序开发入门(2)
- iPhone网络编程初体验-简单的聊天程序(适合新手学习客户端服务器交互)
- C# 网络编程之Tcp实现客户端和服务器聊天
- C#网络编程之 TCP实现客户端和服务器聊天
- 逆向分析人员常用的快捷键
- Endnote x7.5 破解 注册 激活方法
- JSONObject/JSONArray解析json
- 解方程
- 第四周项目3 - 单链表应用(1)
- 服务器/客户端简单实现(初学网络编程)
- Red Black Tree
- 很好的学习python的网站
- provider标签内容提供器
- Qt之自定义提示框(类似QMessageBox)
- 回顾JavaSE(2)-String(1)API构造方法
- iOS10权限问题
- mongoose克隆对象的问题
- LeetCode---18. 4Sum