网络编程预备知识:
主机间通讯所需要的必要步骤:
1、OS(opertion system)
2、IP地址(在互联网中唯一标识一台主机)
3、OS上分配给进程的端口(如:8080)
4、网络传输协议、网络芯片驱动(底层驱动)、传输层统一字节序
OSI七层网络模型 TCP/IP四层网络模型
应用层
端口分配 表示层
会话层 应用层
误差控制 传输层 传输层
网间通讯 网络层 网络层
数据链路层
网卡驱动 物理层 物理层
TCP/IP协议簇:很多协议构成网络协议,相对较为重要的有几个:TCP(传输控制)、IP(网间)、UDP(数据包协议)、IGMP(组管理)、ICMP(控制信息协议);
网络中常用的协议是TCP,UDP也经常使用,TCP相对于UDP多了三次握手四次挥手(链接指定主机必要步骤,提供可靠依据),UDP则不会,因此UDP也称为不可靠传输。
关于网络字节序:
由于数据传输需要在不同的硬件之间,而不同硬件平台所使用的架构不尽相同,如:X86架构使用的是小端序,RSIC则使用大端序(默认是小端序,除非设置一下)。故网络传输需要统一数据存储方式,来完成不同平台的通讯。
Linux中,内核提供了对网络的统一操作接口,也称网络I/O操作。
socket操作网络I/O,特殊的文件描述符(就是利用套接字文件和Linux内置各层协议进行数据缓冲而后实现接收发送(类似于文件读取))。
服务单进程情况下,最多可以连接1021个客户端,在多进程或者多线程的情况下,可以达到多客户端同时处理的效果,但是由于进程和线程都参与系统调度,过多会极大降低系统的响应速度,耗费系统资源(调度器缓存、临时文件读取查找等各种骚操作)。
基于TCP/IP的客户端、服务端模型如下:
服务端:socket–>bind–>listen–>accept–>send/recv–>close(每个流程都是函数名)
客户端:socket–>connect–>send/recv/close(同上)
以上只是基本的流程,并且要注意的是,在服务端的运行过程中,会产生两个临时的套接字文件,虽然看不到,但是也应该清楚一个文件是用于连接的,等到连接了后,这个文件还在,如果没有继续扫描文件动态的话,服务器就进入收发数据阶段,一般情况的测试代码都是让服务器和客户端在while(1)这个死循环中持续地收发数据,因此基本不会执行到close那个函数。
基于UDP/IP协议的服务端、客户端模型如下:
服务端:socket–>bind–>sendto/recvfrom–>close
客户端:socket–>bind–>sendto/recvfrom–>close
对比TCP/IP和UDP/IP模型,其中的三次握手四次挥手被忽略,因此UDP被称为不可靠连接,不过TCP/IP的严格传输要求服务器需要占用资源实时响应,加大服务器端的负载,因此在实际的使用中,还是两个一同使用,混合编程取其长处避其短处。
多路客户端连接
一个服务端连接多个客户端进行同时服务,实现方法大致如下:
1、创建子进程;
2、创建线程;
3、多路I/O复用。(select)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//system includes
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define BUF_SIZE 200
int main(int argc,char *argv[])
{
struct sockaddr_in server_online;//用于连接服务
//创建套接字
int server_temp=socket(AF_INET,SOCK_STREAM,0);
if(-1 == server_temp)
{
printf("套接字创建失败!!\r\n");
return -1;
}
memset(&server_online,0,sizeof(struct sockaddr_in));
server_online.sin_family=AF_INET;
server_online.sin_port = htons(8080);
server_online.sin_addr.s_addr = inet_addr("0.0.0.0");
int result=bind(server_temp,(struct sockaddr *)&server_online,sizeof(struct sockaddr));
if(-1 == result)
{
printf("绑定失败!!\r\n");
return -1;
}
result = listen(server_temp,10);
if(-1 == result)
{
printf("监听创建失败!!\r\n");
return -1;
}
printf("正在扫描端口!!\r\n");
struct sockaddr_in temp;
char buf[BUF_SIZE]="";
socklen_t lenth=sizeof(struct sockaddr_in);
while(1)
{
memset(&temp,0,sizeof(struct sockaddr_in));
int ret=accept(server_temp,(struct sockaddr *)&temp,&lenth);//创建收发数据用的文件描述符
if(-1 == result)
{
printf("连接错误,未能与客户端建立连接!!\r\n");
return -1;
}
printf("客户端连接!!\r\n");
if(0 == fork())
{
while(1)
{
memset(buf,0,BUF_SIZE);
result=recv(ret,buf,BUF_SIZE,0);
if(-1 == result)
{
printf("Receive Error!!!\r\n");
continue;
}
printf("Receive Message %d:%s\r\n",strlen(buf),buf);
result=send(ret,buf,BUF_SIZE,0);
if(-1 == result)
{
printf("Send Message Error!!\r\n");
continue;
}
}
exit(0);
}
close(ret);
}
return 0;
}
客户端代码
#include <stdio#include <stdlib.h>
#include <string.h>
//system includes
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#define BUF_SIZE 200
int main(int argc,char *argv[])
{
int client_temp=socket(AF_INET,SOCK_STREAM,0);
char buf[BUF_SIZE]="";
if(-1 == client_temp)
{
printf("创建套接字失败!!\r\n");
return -1;
}
struct sockaddr_in server_online;
memset(&server_online,0,sizeof(struct sockaddr_in));
server_online.sin_family=AF_INET;
server_online.sin_port=htons(8080);
server_online.sin_addr.s_addr=inet_addr("127.0.0.1");//配置连接服务器参数
int result=connect(client_temp,(struct sockaddr *)&server_online,sizeof(struct sockaddr));//连接服务器
if(-1 == result)
{
printf("连接失败,请重试!!\r\n");
return -1;
}
while(1)
{
memset(buf,0,BUF_SIZE);
printf("Your Message:\r\n");
gets(buf);
result = send(client_temp,buf,BUF_SIZE,0);
if(-1 == result)
{
printf("未发送,请重试!!\r\n");
continue;
}
memset(buf,0,BUF_SIZE);
result = recv(client_temp,buf,BUF_SIZE,0);
if(-1 == result)
{
printf("接收失败,请重试!!\r\n");
continue;
}
printf("Receive Message %d:%s\r\n",strlen(buf),buf);
}
return 0;
}
组播和广播
组播和广播都是利用网络中的特殊IP地址来进行的,其编程涉及到IP地址的规定:
规定中说明了IP地址分为五大类:
A、B、C、D、E(也不知道为什么这么草率地分类,起码有个名字好记点)
每个类中都有其区段(数字范围)
A:1.0.0.1~126.255.255.254
B:128.0.0.1~191.255.255.254
C:192.0.0.1~233.255.255.254
D:组播
E:保留
组播和广播分别向以下网段发送信息就可以实现:
广播:局域网:192.255.255.255
组播:224.0.0.1~239.255.255.254
unix域套接字
用于本地通讯,可以实现前后台进程交换数据:
流式:
服务端:socket–>listen–>accept–>recv/send
客户端:socket–>connect–>recv/send
数据报:
客户端/服务端:socket–>bind–>recvfrom/sendto
epoll高容量服务器模型
这个是Linux内核的优势,是由系统提供的大型服务器解决方案,是select的升级版本,解除select受描述符集合范围的限制。
epoll主要的操作函数有:文件–>sys/epoll.h
1、创建一个epoll的文件描述符:int epoll_create(int size);
2、注册epoll到系统:int epoll _ ctl (int epfd, int op
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>
#define IPADDRESS "127.0.0.1"
#define PORT 8787
#define MAXSIZE 1024
#define LISTENQ 5
#define FDSIZE 1000
#define EPOLLEVENTS 100
//函数声明
//创建套接字并进行绑定
static int socket_bind(const char* ip,int port);
//IO多路复用epoll
static void do_epoll(int listenfd);
//事件处理函数
static void
handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf);
//处理接收到的连接
static void handle_accpet(int epollfd,int listenfd);
//读处理
static void do_read(int epollfd,int fd,char *buf);
//写处理
static void do_write(int epollfd,int fd,char *buf);
//添加事件
static void add_event(int epollfd,int fd,int state);
//修改事件
static void modify_event(int epollfd,int fd,int state);
//删除事件
static void delete_event(int epollfd,int fd,int state);
int main(int argc,char *argv[])
{
int listenfd;
listenfd = socket_bind(IPADDRESS,PORT);
listen(listenfd,LISTENQ);
do_epoll(listenfd);
return 0;
}
static int socket_bind(const char* ip,int port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
perror("socket error:");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET,ip,&servaddr.sin_addr);
servaddr.sin_port = htons(port);
if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
return listenfd;
}
static void do_epoll(int listenfd)
{
int epollfd;
struct epoll_event events[EPOLLEVENTS];
int ret;
char buf[MAXSIZE];
memset(buf,0,MAXSIZE);
//创建一个描述符
epollfd = epoll_create(FDSIZE);
//添加监听描述符事件
add_event(epollfd,listenfd,EPOLLIN);
for ( ; ; )
{
//获取已经准备好的描述符事件
ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
handle_events(epollfd,events,ret,listenfd,buf);
}
close(epollfd);
}
static void
handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
{
int i;
int fd;
//进行选好遍历
for (i = 0;i < num;i++)
{
fd = events[i].data.fd;
//根据描述符的类型和事件类型进行处理
if ((fd == listenfd) &&(events[i].events & EPOLLIN))
handle_accpet(epollfd,listenfd);
else if (events[i].events & EPOLLIN)
do_read(epollfd,fd,buf);
else if (events[i].events & EPOLLOUT)
do_write(epollfd,fd,buf);
}
}
static void handle_accpet(int epollfd,int listenfd)
{
int clifd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
if (clifd == -1)
perror("accpet error:");
else
{
printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
//添加一个客户描述符和事件
add_event(epollfd,clifd,EPOLLIN);
}
}
static void do_read(int epollfd,int fd,char *buf)
{
int nread;
nread = read(fd,buf,MAXSIZE);
if (nread == -1)
{
perror("read error:");
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else if (nread == 0)
{
fprintf(stderr,"client close.\n");
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else
{
printf("read message is : %s",buf);
//修改描述符对应的事件,由读改为写
modify_event(epollfd,fd,EPOLLOUT);
}
}
static void do_write(int epollfd,int fd,char *buf)
{
int nwrite;
nwrite = write(fd,buf,strlen(buf));
if (nwrite == -1)
{
perror("write error:");
close(fd);
delete_event(epollfd,fd,EPOLLOUT);
}
else
modify_event(epollfd,fd,EPOLLIN);
memset(buf,0,MAXSIZE);
}
static void add_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}
static void delete_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
static void modify_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}
客户端代码模型:
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define MAXSIZE 1024
#define IPADDRESS "127.0.0.1"
#define SERV_PORT 8787
#define FDSIZE 1024
#define EPOLLEVENTS 20
static void handle_connection(int sockfd);
static void
handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf);
static void do_read(int epollfd,int fd,int sockfd,char *buf);
static void do_read(int epollfd,int fd,int sockfd,char *buf);
static void do_write(int epollfd,int fd,int sockfd,char *buf);
static void add_event(int epollfd,int fd,int state);
static void delete_event(int epollfd,int fd,int state);
static void modify_event(int epollfd,int fd,int state);
int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//处理连接
handle_connection(sockfd);
close(sockfd);
return 0;
}
static void handle_connection(int sockfd)
{
int epollfd;
struct epoll_event events[EPOLLEVENTS];
char buf[MAXSIZE];
int ret;
epollfd = epoll_create(FDSIZE);
add_event(epollfd,STDIN_FILENO,EPOLLIN);
for ( ; ; )
{
ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
handle_events(epollfd,events,ret,sockfd,buf);
}
close(epollfd);
}
static void
handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf)
{
int fd;
int i;
for (i = 0;i < num;i++)
{
fd = events[i].data.fd;
if (events[i].events & EPOLLIN)
do_read(epollfd,fd,sockfd,buf);
else if (events[i].events & EPOLLOUT)
do_write(epollfd,fd,sockfd,buf);
}
}
static void do_read(int epollfd,int fd,int sockfd,char *buf)
{
int nread;
nread = read(fd,buf,MAXSIZE);
if (nread == -1)
{
perror("read error:");
close(fd);
}
else if (nread == 0)
{
fprintf(stderr,"server close.\n");
close(fd);
}
else
{
if (fd == STDIN_FILENO)
add_event(epollfd,sockfd,EPOLLOUT);
else
{
delete_event(epollfd,sockfd,EPOLLIN);
add_event(epollfd,STDOUT_FILENO,EPOLLOUT);
}
}
}
static void do_write(int epollfd,int fd,int sockfd,char *buf)
{
int nwrite;
nwrite = write(fd,buf,strlen(buf));
if (nwrite == -1)
{
perror("write error:");
close(fd);
}
else
{
if (fd == STDOUT_FILENO)
delete_event(epollfd,fd,EPOLLOUT);
else
modify_event(epollfd,fd,EPOLLIN);
}
memset(buf,0,MAXSIZE);
}
static void add_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}
static void delete_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
static void modify_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}
代码源参考自:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html