- 浏览: 871222 次
文章分类
最新评论
-
angel6709:
怎么下载啊
Android 蓝牙开发实例--蓝牙聊天程序的设计和实现 -
再_见孙悟空:
能识别中文不?
借@阿里巴巴 耍了个帅——HTML5 JavaScript实现图片文字识别与提取 -
在下个路口:
连接时总是报Unable to start Service D ...
Android 蓝牙开发实例--蓝牙聊天程序的设计和实现 -
mike.liu:
如果在社会上,站在政府的立场思考问题,是会被人说5毛的。但是在 ...
项目预估激发的矛盾 -
kjmmlzq19851226:
我的意思是说,公司采取两种策略:
1. 每月4k的基本工资,另 ...
项目预估激发的矛盾
Linux 网络 UDP TCP select模式 http协议
网络:
1.网络的基本概念
网络编程采用socket模型
网络通信本质也是进程之间的通信,是不同主机之间
识别主机:4字节整数 :ip地址
识别进程:2字节整数 :端口号
IP地址的表示方法: 内部表示:4字节整数
外部表示:数点字符串
结构体
1 2 3 4 分段表示,每个段使用.分割
"192.168.0.26"
ip地址转换:
#include<netinet/in.h>
structsockaddr_in
{
intsin_family
in_port_tsin_port;
structin_addr sin_addr;
};
struct in_addr
{
in_addr_ts_addr;
};
总结:
ip地址的表示
字符串表示“192.168.0.26”
整数表示: in_addr_t
结构体表示:struct in_adddr
连接点:endpoint
struct sockaddr_in
{
in_port_t sin_port;
strcutin_addr sin_addr;
};
Ip地址的转换:
#include<arpa/inet.h>
inet_addr//把字符串转化为整数(网络字节序)
inet_aton//把字符串转化为structin_addr(网络字节序)
inet_network //把结构体转换为字符串(本地字节序)
inet_ntoa//把结构体转换为字符串
例子1:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
in_addr_t nip=192<<24|168<<16|0<<8|26;
char *ip= “192.168.0.26”;
int myip;
//把整数转换为字符串inet_ntoa
struct in_addr sip={nip};
printf(“%s\n”,inet_ntoa(sip));
//把字符串转换为整数
myip=inet_addr(ip);
printf(“%u\n”,myip);
}
inet_lnaof inet_netof 函数:
从ip地址得到主机标示位,和网络标示位
函数inet_lnaof函数是将套接口地址中的IP地址(网络字节序)转换为没有网络位的主机ID(主机字节序)
in_addr_t
inet_lnaof(struct
in_addr addr);
in_addr_tinet_netof(struct in_addr addr);
代码例子:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
char *ip= “192.168.0.26”;
struct in_addr addr;
in_addr_t net;
int_addr_t host;
struct in_addr tmp;
inet_aton(ip,&addr); //
转换为网络字节序
host=inet_lnaof(addr);
net=inet_netof(addr);
tem.s_addr=net;
printf(“%s\n”,inet_ntoa(tem));
tem.s_addr=host;
printf(“%s\n”,inet_ntoa(tem));
}
TCP/UDP
编程
对等模型:AF_INET SOCK_DGRAM对应的默认协议 :0 :UDP
c/s
模型:AF_INET SOCK_STREAM 0 :TCP
网络编程:
IOS
的7层模型:
物理层
数据链路层 数据链路层(数据物理怎么传输)
网络层 IP层 (数据的传输方式)
传输层 传输层 (数据传输的结果)
会话层 应用层 (数据传递的含义)
表示层
应用层
UDP
编程的数据特点
UDP
采用对等模型SOCK_DGRAM
socket socket
绑定ip地址bind 连接目标(可选)connect
read/recv/recvfrom
发送数据write/send/sendto
关闭close
recv
函数:
ssize_t recv(int s, void * buf,size_tlen ,int flags);
参数3:指定为0时和read函数一样(接收不到数据阻塞等待)
recvfrom
函数:
ssize_t recvfrom(int s,void *buf,size_t len ,intflags
struct sockaddr*from,socklen_t *fromlen);
参数4,5:为发送者的ip地址和地址长度
send
函数
sendto
函数
int sendto(
int fd,//socket
描述符号
const void*buf,//
发送的数据缓冲
size_t size,//
发送的数据长度
int flags,//
发送方式MSG_NOWAITMSG_OOB
const structsockaddr *addr,//
发送的目标的IP与端口
socklen_tlen//sockaddr_in
的长度
);
返回:
-1:
发送失败
>=0:
发送的数据长度
案例:
A
: B:
接收用户数据 发送数据
打印数据与发送者的ip 接收数据
返发一个消息 打印
代码:
UdpA.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
void main()
{
intfd; //socket
描述符
struct sockaddr_in ad;//
本机的ip地址
char buf[100]; //
接收数据缓存
struct sockaddr_in ad_snd; //
发送者ip地址
intr;
socklen_t len;//
发送者ip长度
fd=socket(AF_INET , SOCK_DGRAM ,0); //0
默认代表UDP协议,
//17
也代表UDP协议
if(fd== -1) printf(“socket:%m\n”),exit(-1);
printf(“
建立socket成功!\n”);
ad.sin_family=AF_INET;
ad.sin_port=htons(11111);
inet_aton(“127.0.0.1”, &ad.sin_addr);
r=bind(fd,(structsockaddr*)&ad,sizeof(ad));
if(r==-1) printf(“bind:%m\n”),exit(-1);
printf(“
绑定成功!\n”);
while(1)
{
len=sizeof(ad_snd);
r=recvfrom(fd,buf,sizeof(buf),0,
(struct sockaddr*)&ad_snd,&len);
sendto(fd, “
收到信息”,strlen(“收到信息”),0,
(structsockaddr*)&ad_snd,sizeof(ad_snd));
if(r>0)
{
Buf[r]=0;
printf(“
发送者IP:%s,数据:%s\n”,
inet_ntoa(ad_snd.sin_addr),buf);
}
if(r==0)
{
printf(“
关闭!\n”);
close(fd);
break;
}
if(r==-1)
{
printf(“
网络故障!\n”);
close(fd);
break;
}
}
close(fd);
}
udpB.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intfd;
struct sockadddr_in ad;
char buf[102];
int r;
fd=socket(AF_INET,SOCK_DGRAM,0);
if(fd==-1) printf(“socket err:%m\n”),exit(-1);
ad.sin_family=AF_INET;
ad.sin_port=htons(11111);
ad.sin_addr.s_addr=inet_addr(“127.0.0.1”);
while(1)
{
r=read(0,buf,sizeof(buf)-1);
if(r<=0) break;
buf[r]= ‘\0’;
r=sendto(fd,buf,r,0,(structsockaddr*)&ad,sizeof(ad));
if(r==-1)break;
bzero(buf,sizeof(buf));
r=recv(fd,buf,sizeof(buf),0);
buf[r]= ‘\0’;
printf(“
来自接收方的数据:%s\n”,buf);
}
close(fd);
}
总结:1.connect+send==sendto
2.
recvfrom
的作用不是专门从指定ip接收
而是从任意ip接收数据,返回发送者ip地址
3.
为什么要bind,bind主要目的告诉网络发送目标
4.
是否一定绑定才能发送数据?
否:只要知道你的ip和端口,就能发送数据
5.
为什么发送者没有绑定ip和端口,它也有端口
底层网络驱动,帮我们自动生成ip与端口
TCP
编程的数据特点
TCP
的编程模型:
案例1:
TCP
的服务器(在案例中使用浏览器作为客户程序)
socket
建立服务器的文件描述符号缓冲
bind
把IP地址与端口设置到文件描述符号中
listen
负责根据客户连接的不同IP与端口,负责生成对应的文件描述符号及其信息
accept
一旦listen有新的描述符号产生就返回,否则阻塞。
代码:
Tcp_sev.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intserverfd;
intcfd;
struct sockaddr_in saddr;
struct sockaddr_in caddr;
int r;
socklen_t len;
//1.socket
serverfd=socket(AF_INET,SOCK_STREAM,0);
if(serverfd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立服务器socket成功!\n”);
//2.bind
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&saddr.sin_addr);
r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
服务器地址绑定成功!\n”);
//3.listen
r=listen(serverfd,10);//
参数1:服务器sock描述符,参数2:连//接最大数
if(r==-1) printf(listen:%m\n),exit(-1);
printf(“
监听服务器成功!\n”);
//4.accept
len=sizeof(caddr);
While(1)
{
cfd=accept(serverfd,(structsockaddr*)&caddr,&len);
//
参数1:服务器端socket描述符,参数2:返回客户端地址,//参数3:返回客户端地址实际的长度(传入和返回双向参数)
printf(“
有人连接:%d,IP:%s,%u\n”,
cfd,inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port));
}
}
客户端:浏览器地址栏输入:http://127.0.0.1:9999
服务器端会显示浏览器连接服务器端
案例2:
每个客户的代理描述符号的通信(客户端浏览器)
通过accept返回的描述符进行通信
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intserverfd;
intcfd;
struct sockaddr_in saddr;
struct sockaddr_in caddr;
int r;
socklen_t len;
char buf[1024];
//1.socket
serverfd=socket(AF_INET,SOCK_STREAM,0);
if(serverfd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立服务器socket成功!\n”);
//2.bind
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&saddr.sin_addr);
r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
服务器地址绑定成功!\n”);
//3.listen
r=listen(serverfd,10);//
参数1:服务器sock描述符,参数2:连//接最大数
if(r==-1) printf(listen:%m\n),exit(-1);
printf(“
监听服务器成功!\n”);
//4.accept
len=sizeof(caddr);
cfd=accept(serverfd,(struct sockaddr*)&caddr,&len);
//
参数1:服务器端socket描述符,参数2:返回客户端地址,
//
参数3:返回客户端地址实际的长度(传入和返回双向参数)
printf(“
有人连接:%d,IP:%s,%u\n”,
cfd,inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port));
//5.
处理代理客户描述符号的数据
r=recv(cfd,buf,1024,0);
if(r>0)
{
buf[r]= ‘\0’;
printf(“::%s\n”,buf);
}
if(r==0)
{
printf(“
连接断开!\n”);
}
if(r==-1)
{
Printf(“
网络故障!\n”);
}
}
//
客户端:浏览器地址栏:http://127.0.0.1:9999/index.html
服务器端会接收到数据,并打印
TCP
通信特点(相对UDP)
案例3:
有连接:主要连接后,发生数据不用指定IP与端口
数据无边界:TCP数据流,非数据报文
数据准确:TCP协议保证数据完全正确
代码:
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intserverfd;
intcfd;
struct sockaddr_in saddr;
struct sockaddr_in caddr;
int r;
socklen_t len;
char buf[1024];
//1.socket
serverfd=socket(AF_INET,SOCK_STREAM,0);
if(serverfd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立服务器socket成功!\n”);
//2.bind
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&saddr.sin_addr);
r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
服务器地址绑定成功!\n”);
//3.listen
r=listen(serverfd,10);//
参数1:服务器sock描述符,参数2:连//接最大数
if(r==-1) printf(listen:%m\n),exit(-1);
printf(“
监听服务器成功!\n”);
//4.accept
len=sizeof(caddr);
cfd=accept(serverfd,(structsockaddr*)&caddr,&len);
//
参数1:服务器端socket描述符,参数2:返回客户端地址,
//
参数3:返回客户端地址实际的长度(传入和返回双向参数)
printf(“
有人连接:%d,IP:%s,%u\n”,
cfd,inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port));
//5.
处理代理客户描述符号的数据
while(1)
{
r=recv(cfd,buf,1024,0);
if(r>0)
{
buf[r]= ‘\0’;
printf(“::%s\n”,buf);
}
if(r==0)
{
printf(“
连接断开!\n”);
}
if(r==-1)
{
Printf(“
网络故障!\n”);
}
}
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intfd;
struct sockaddr_in addr;
int r;
//1.socket
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立客户端socket成功!\n”);
//2.connect
addr.sin_family=AF_INET;
addr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&addr.sin_addr);
r=connect(fd,(structsockaddr*)&addr,sizeof(addr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
连接服务器成功!\n”);
for(i=0;i<20;i++)
{
r=send(fd,“hello”,5,0);
}
}
总结:客户端循环发生20次hello,服务器端接收的数据,并不是分20次接收20个hello。说明:TCP是通过流式数据发生的,而不是数据报文。
而UDP是通过数据报文传送的,是分20次接收20个hello
recv
函数的flags参数:
MSG_WAITALL
:一定要把缓冲填满在发送
TCP
流式数据传送(数据无边界),有可能缓冲没有填满,数据就发送了。
案例4:
使用TCP发送数据注意:
不要以为固定长的数据,一定接收正确,要使用MSG_WAITALL使固定长的数据接收正确。
代码:
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intserverfd;
intcfd;
struct sockaddr_in saddr;
struct sockaddr_in caddr;
int r;
socklen_t len;
int a;
//1.socket
serverfd=socket(AF_INET,SOCK_STREAM,0);
if(serverfd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立服务器socket成功!\n”);
//2.bind
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&saddr.sin_addr);
r=bind(serverfd,(structsockaddr*)&saddr,sizeof(saddr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
服务器地址绑定成功!\n”);
//3.listen
r=listen(serverfd,10);//
参数1:服务器sock描述符,参数2:连//接最大数
if(r==-1) printf(listen:%m\n),exit(-1);
printf(“
监听服务器成功!\n”);
//4.accept
len=sizeof(caddr);
cfd=accept(serverfd,(structsockaddr*)&caddr,&len);
//
参数1:服务器端socket描述符,参数2:返回客户端地址,
//
参数3:返回客户端地址实际的长度(传入和返回双向参数)
printf(“
有人连接:%d,IP:%s,%u\n”,
cfd,inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port));
//5.
处理代理客户描述符号的数据
while(1)
{
r=recv(cfd,&a,4,MSG_WAITALL);
if(r>0)
{
buf[r]= ‘\0’;
printf(“::%s\n”,buf);
}
if(r==0)
{
printf(“
连接断开!\n”);
}
if(r==-1)
{
Printf(“
网络故障!\n”);
}
}
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intfd;
struct sockaddr_in addr;
int r;
//1.socket
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1) printf(“socket:%m\n”),exit(-1);
printf(“
建立客户端socket成功!\n”);
//2.connect
addr.sin_family=AF_INET;
addr.sin_port=htons(9999);
inet_aton(“127.0.0.1”,&addr.sin_addr);
r=connect(fd,(structsockaddr*)&addr,sizeof(addr));
if(r==-1) printf(“bind :%m\n”),exit(-1);
printf(“
连接服务器成功!\n”);
for(i=0;i<20;i++)
{
r=send(fd,&i,4,0); //
发送i的整数
}
}
总结:当客户端发送整数时,因为TCP是流式数据无边界,发送数据时,缓冲有可能填不满就发送给服务器端了,这使得服务器端接收的数据有误。
解决方案:recv函数的flags参数使用MSG_WAITALL标记,这样没回发送数据的缓冲都会被填满在发送,这样服务器端就能正确接收整数了。
案例5:
TCP
数据发送的分析:
基本数据intshort long float double
结构体数据struct
建议使用MSG_WAITALL(固定长)
字符串数据以及文件数据等不固定长度的数据怎么发送?
指定数据包:
头:大小固定(存放数据大小)
体:大小变化(数据)
使用TCP传送文件
定义文件数据包
int
数据大小
char[]
数据
传递文件名
传递数据(循环)
传递0长度的数据表示文件结束
代码:
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
void main()
{
int sfd;
int ffd;
int size;
int r;
int len;
char buf[128];
struct sockaddr_in dr;
char filename[]= “udp_a.c”;
//1
建立socket
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1) printf(“:%m\n”),exit(-1);
printf(“socket
成功!\n”);
//2
连接到服务器
dr.sin_family=AF_INET;
dr.sin_port=htons(9988);
inet_aton(“127.0.0.1”,&dr.sin_addr);
r=connect(sfd,(structsockaddr*)&dr,sizeof(dr));
if(r==-1)printf(:%m\n),exit(-1);
printf(“connect
成功!\n”);
//3
发送文件名
len=strlen(filename);
r=send(sfd,&len,sizeof(len),0);
if(r==-1)printf(“:%m\n”),exit(-1);
r=send(sfd,filename,len,0);
//4
打开文件
ffd=open(filename,O_RDONLY);
if(ffd==-1)printf(“:%m\n”),exit(-1);
printf(“open
文件成功!\n”);
//5
循环发送数据
while(1)
{
size=read(ffd,buf,128);
if(size==-1)break;
if(size==0) break;
if(size>0)
{
r=send(sfd,&size,sizeof(size),0);//
发送数据长度
if(r==-1)break;
r=send(sfd,buf,size,0);//
发送数据
if(r==-1)break;
}
}
//6
读取到文件尾,发送0的数据包
size=0;
r=send(sfd,&size,sizeof(size),0);
close(ffd);
close(sfd);
printf(“ok\n”);
}
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<aarpa/inet.h>
#include<fcntl.h>
void main()
{
int sfd,cfd,ffd;
int r;
int len;
char buf[128];
char filename[100];
struct sockaddr_in dr;
//1
建立服务器socket
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1)printf(“:%m\n”),exit(-1);
printf(“
建立服务器成功!\n”);
//2
绑定ip地址端口
dr.sin_family=AF_INET;
dr.sin_port=htons(9988);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=bind(sfd,(structsockaddr*)&dr,sizeof(dr));
if(r==-1)printf(“:%m\n”),exit(-1);
printf(“
绑定地址成功!\n”);
//3
监听
r=listen(sfd,10);
if(r==-1)printf(“:%m\n”),exit(-1);
printf(“
监听成功!\n”);
//4
接收连接
cfd=accept(sfd,0,0);
if(cfd==-1)printf(“:%m\n”),exit(-1);
printf(“
开始接收文件\n”);
//
接收文件名
r=recv(cfd,&len,sizeof(len),MSG_WAITALL);
r=recv(cfd,filename,len,MSG_WAITALL);
filename[len]= ‘\0’;
printf(“
传递的文件名是:%s\n”,filename);
//6
创建文件
ffd=open(filename,O_RDWR|O_CREAT,0666);
//7
循环接收文件数据
while(1)
{
r=recv(cfd,&len,sizeof(len),MSG_WAITALL);
if(len==0) break;
r=recv(cfd,buf,len,MSG_WAITALL);
write(ffd,buf,len);
}
close(ffd);
close(cfd);
close(sfd);
printf(“
接收文件完成!\n”);
}
TCP
的服务器的编程
TCP
的服务器端维护多个客户的网络文件描述符。
对服务器多个客户描述符同时做读操作,是不可能的,需要使多任务
多任务模型?
1.
多进程
2.
IO
的异步模式(select模式/poll模式)
3.
多线程模式
4.
多进程池
5.
多线程池
综合应用--多进程应用
1.
怎样使用多进程
2.
多进程的缺陷,以及怎么解决
客户端
1.
建立socket
2.
连接服务器
3.
创建子进程
4.
在父进程中,输入,发送聊天信息
5.
在子进程中,接收服务器传递其他客户聊天信息
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
intfd;
intr;
struct sockaddr_in dr;
int initSocket();
void destroy;
voidhandle(int s)
{
int status;
wait(&status);
destroy();
exit(-1);
}
voidmain()
{
fd=initSocket();
if(fd== -1) printf(“连接网络失败!\n”),exit(-1);
printf(“连接网络\n”);
signal(SIGCHLD,handle);
if(fork())
{
//输入,发送
char buf[256];
while(1)
{
printf(“请输入消息:\n”);
scanf(“%s”,buf);
if(strcmp(buf, “1”)) break;
send(fd,buf,strlen(buf),0);
}
}
else
{
//接收,显示
char buf[256];
while(1)
{
r=recv(fd,buf,255,0);
buf[r]= ‘\0’;
printf(“MSG:%s\n”,buf);
}
}
destroy();
}
intinitSocket()
{
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd== -1)return -1;
dr.sin_family=AF_INET;
dr.sin_port=htons(9988);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=connect(fd,(structsockaddr*)&dr,sizeof(dr));
if(r== -1)
{
close(fd);
return -1;
}
return fd;
}
void destroy()
{
close(fd);
}
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/men.h>
intsfd;
int*fds;//存放所以客户代理描述符号
intidx=0;//客户在数组中的下标
structsockaddr_in dr;
intr;
voidmain()
{
fds=mmap(0,4*100,PROT_READ|PROT_WRITE|MAP_ANONYMOUS|MAP_SHARED,0,0);
//1建立服务器socket
bzero(fds,sizeof(fds));
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd== -1) printf(“:%m\n”),exit(-1);
printf(“socket ok \n”);
//2绑定地址
dr.sin_family=AF_INET;
dr.sin_port=htons(9988);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
if(r== -1) printf(“:%m\n”) ,exit(-1);
printf(“bind ok\n”);
//3监听
r=listen(sfd,10);
if(r== -1) printf(“:%m\n”),exit(-1);
//4循环接收客户连接
while(1)
{
fds[idx]=accept(sfd,0,0);
if(fds[idx]= = -1) break;
printf(“有客户连接\n”);
//5建立一个子进程
if(fork())
{
idx++;
continue;
}
else
{
//6.子进程任务:接收客户数据且广播
char buf[256];
int i;
while(1)
{
//接收客户数据
r=recv(fds[idx],buf,255,0);
if(r==0)
{
printf(“有客户退出\n”);
close(fds[idx]);
fds[idx]=0;
break;
}
if(r== -1)
{
printf(“网络故障\n”);
close(fds[idx]);
fds[idx]=0;
break;
}
buf[r]= ‘\0’;
printf(“来自客户的数据:%s\n”,buf);
//广播
for(i=0;i<100;i++)
{
if(fds[i]>0)
{
send(fds[i],buf,r,0);
}
}
}
exit(0);
}
}
}
总结:
建立socket
绑定地址
监听
循环接收客户连接
为客户创建子进程
在子进程接收该客户的数据,并且广播
问题:多进程由于进程资源结构独立。
新进程的文件描述符号的环境在老进程中是无法访问的。
SELECTTCP服务器模式:
1. select函数:通过异步方式中断处理文件描述符
#include<sys/select.h>
int select(
int fds,//建议是监控的文件描述符号的最大值+1
fd_set *readfds,//读文件描述符号集合
//该参数既是输入,也是输出
//输入:被监控的描述符号
//输出:有数据的描述符号
fd_set *writefds,
fd_set *errfds,
struct timeval*timeout);//指定阻塞时间限制
//为NULL,永久
返回:
>0:发生改变的文件描述符号个数
=0:时间限制过期
=-1:异常
2.IO能否发出信号?
异步IO就是通过信号工作.
例子:
#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<unistd.h>
void handle(ints)
{
charbuf[200];
intr;
r=read(0,buf,199);
buf[r]= ‘\0’;
printf(“::%s\n”,buf);
}
void main()
{
fcntl(0,F_SETFL,O_ASYNC); //设置输入io可发出信号(当有输入时)
//O_ASYNC当I/O可用的时候,允许SIGIO信号发送到进程组
fcntl(0,F_SETOWN,getpid()); //设置输入io描述符号有变化SIGIO信号发给本进程
signal(SIGIO,handle);
while(1); //进程执行死循环
}
异步io:fcntl函数设置输入io为异步io,该进程执行死循环,当输入io的文件描述符有变化(有输入发生)时,io就会发出信号SIGIO,死循环就会停止执行,并执行信号处理函数,信号处理函数执行完毕后,死循环再次继续执行。
Select的结构:
Select管理描述符集合,设置描述符为异步,并设置信号处理,执行死循环等待描述符的改变。
3.select应用:
#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<unistd.h>
#include<sys/select.h>
void main()
{
fd_setfds;
intr;
char buf[100];
while(1)
{
FD_ZERO(&fds); //FD_ZERO函数宏,清空文件描述符集合
FD_SET(0,&fds); //FD_SET函数宏,添加文件描述符到描述符集合中
r=select(1,&fds,0,0,0);
printf(“有数据输入\n”);
r=read(0,buf,99);
printf(“%s\n”,buf);
}
}
4.使用select实现TCP的多客户连接与处理
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
void main()
{
intsfd; //服务器描述符
intfdall[100]; //客户描述符
intcount; //客户的个数
intr; //返回值(异常处理)
structsockaddr_in dr; //ip地址与端口
fd_setfds; //被select监控的描述符集合
intmaxfd; //最大文件描述符号
charbuf[1024];
inti,j; //循环变量
//1.建立socket
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd== -1) printf(“:%m\n”),exit(-1);
//2.绑定地址与端口
dr.sin_family=AF_INET;
dr.sin_port=htons(8866);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=bind(sfd,(struct sockaddr *)&dr,sizeof(dr));
if(r == -1) printf(“:%m\n”),close(sfd),exit(-1);
//3.监听
r=listen(sfd,10);
if(r== -1) printf(“:%m\n”),close(sfd),exit(-1);
count=0;
maxfd=0;
FD_ZERO(&fds);
for(i=0;i<100;i++)
{
fdall[i]= -1;
}
while(1)
{
//4.构造监听的描述符号集合【服务器描述符号,客户描述符集合】
//4.1.清空
FD_ZERO(&fds); //每次都要清空,因为select返回的fds中是有变化的描//述符,而不是所以连接的客户端描述符
maxfd=0;
//4.2加入服务器描述符
FD_SET(sfd,&fds);
maxfd=maxfd>=sfd?maxfd:sfd;
//4.3加入客户描述符
for(i=0;i<count;i++)
{
if(fdall[i]!= -1)
{
FD_SET(fdall[i],&fds);
maxfd=maxfd>=fdall[i]?maxfd:fdall[i];
}
}
//5.使用select循环控制描述符号集合
r=select(maxfd+1,&fds,0,0,0);
//6.分两种情况处理:
//6.1.有客户连接:服务器描述符号
if(FD_ISSET(sfd,&fds))
{
fdall[count]=accept(sfd,0,0);
if(fdall[count]== -1)
{
printf(“服务器崩溃\n”);
exit(-1);
}
printf(“有客户连接\n”);
count++;
}
//6.2.有客户发送数据:客户描述符号
for(i=0;i<count;i++)
{
//判断改变描述号是否存在
if(fdall[i]!= -1 && FD_ISSET(fdall[i],&fds))
{
//读取数据
r=recv(fdall[i],buf,1023,0);
if(r==0)
{
printf(“有客户退出\n”);
close(fdall[i]);
fdall[i]= -1;
}
if(r== -1)
{
printf(“网络故障\n”);
close(fdall[i]);
fdall[i]= -1;
}
if(r>0)
{
buf[r]= ‘\0’;
printf(“广播数据\n”);
//广播数据
for(j=0;j<count;j++)
{
if(fdall[j]!=-1)
{
send(fdall[j],buf,r,0);
}
}
}
}
}
}
}
poll模式:
poll函数:
int poll(
struct pollfd *fds,//监控的描述符号,监听的结构体数组
int nfds,//监控的描述符号的个数
int timeout ); //阻塞超时,-1为永久
struct pollfd{
int fd;//监听的描述符号
short events;//监听的操作POLLIN监听输入,POLLOUT监听输出
short revents;//返回的事件
};
例子代码:
#include<stdio.h>
#include<unistd.h>
#include<sys/poll.h>
void main()
{
struct pollfd fds[1];
intr;
char buf[100];
fds[1].fd=0; //监听的描述符号
fds[1].events=POLLIN; //监听读取
while(1)
{
r=poll(fds,1,-1);
if(fds[0].revents == POLLIN)
{
printf(“有数据输入\n”);
}
r=read(0,buf,99);
printf(“%s\n”,buf);
}
}
poll模式和select模式基本一样,只是使用风格不同
socket选型设置:
通用选项:
SOL_SOCKET
SO_BROADCAST 广播
SO_RCVBUF 描述符号的缓冲大小
SO_SNDBUF 描述符号的缓冲大小
SO_REUSEADDR 地址可多次绑定
SO_TYPE 描述符号类型SOCK_STREAM 等
ICMP选项
IPPROTO_ICMP
ICMP_FILTER
IP选项(干预系统怎么生产ip头)
IPPROTO_IP
UDP选项
IPPROTO_UDP
TCP选项
IPPROTO_TCP
设置选项:setsocket
获取选项:getsocket
#include<sys/socket.h>
intgetsocket(int s , int level ,int optname, void * optval , socklen_t optlen);
intsetsocket(int s ,int level ,int optname , const void * optval,socklen_t optlen);
参数1:socket描述符号
参数2:设置那一层的选项(SOL_SOCKET(通用),IPPROTO_UDP,IPPROTO_TCP 等)
参数3:选项名
参数4:选项值
参数5:值的大小
案例:
判定一个socket的数据类型SOCK_STREAM SOCK_DGRAM SOCK_RAW
代码:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
voidmain()
{
int fd;
int type;
int len;
len=sizeof(type);
fd=socket(AF_INET,SOCK_STREAM,0);
getsocket(fd,SOL_SOCKET,SO_TYPE,&type,&len);
printf(“%u:%u”,SOCK_STREAM,type);
if(type&SOCK_STREAM)
{
printf(“流!\n”);
}
if(type&SOCK_DGRAM)
{
printf(“报文\n”);
}
}
案例2:
获得socket缓冲大小
代码:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
voidmain()
{
int fd;
int type;
int len;
len=sizeof(type);
fd=socket(AF_INET,SOCK_STREAM,0);
getsocket(fd,SOL_SOCKET,SO_RCVBUF,&type,&len);
printf(“缓冲大小:%u\n”,type);
}
案例:
使用选项进行数据广播SO_BROADCAST(对流无效)
cast_A发送
建立socket
设置广播选型
发送数据(广播方式发送)
cast_B接收
建立socket
设置地址可重用选型SO_REUSEADDR
绑定地址
接收数据
代码:
Cast_A.c
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
voidmain()
{
int fd;
int opt=1; //广播操作,1为广播,0为不广播
struct sockaddr_in dr;
fd=socket(AF_INET,SOCK_DGRAM,0);
setsocket(fd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
dr.sin_addr.s_addr=inet_addr(“/*广播地址*/”); //地址不在是目标地址,而是广播地址
sendto(fd, “hello”,5,0,(struct sockaddr*)&dr,sizeof(dr));
}
Cast_B.c
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
voidmain()
{
int fd;
int opt=1; //广播操作,1为广播,0为不广播
struct sockaddr_in dr;
char buf[100];
int r;
fd=socket(AF_INET,SOCK_DGRAM,0);
if(fd == -1) printf(“%m\n”),exit(-1);
r=setsocket(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(r == -1) printf(“%m\n”),exit(-1);
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
dr.sin_addr.s_addr=inet_addr(“/*广播地址*/”); //地址不在是目标地址,而是广播地址
r=bind(fd, (struct sockaddr*)&dr,sizeof(dr));
if(r == -1) printf(“%m\n”),exit(-1);
r=recv(fd,buf,100,0);
buf[r]= ‘\0’;
printf(“接收广播数据:%s\n”,buf);
close(fd);
}
OOB数据(TCP)
优先数据
send(,MSG_OOB)
recv(,MSG_OOB);
案例:
Oob_server.c
Recv MSG_OOB
Oob_client.c
Send MSG_OOB
代码:
Oob_server.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
voidmain()
{
int fd,cfd;
char buf[100];
int r;
struct sockaddr_in dr;
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd == -1) printf(“%m\n”),exit(-1);
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=bind(fd,(structsockaddr*)&dr,sizeof(dr));
if(r == -1) printf(“%m\n”),exit(-1);
r=listen(fd,10);
if(r == -1) printf(“%m\n”),exit(-1);
cfd=accept(fd,0,0);
if(cfd == -1) printf(“%m\n”),exit(-1);
while(1)
{
r=recv(cfd,buf,100,MSG_OOB);
if(r>0)
{
buf[r]= ‘\0’;
printf(“%s\n”,buf);
}
if(r<=0)
break;
}
close(cfd);
close(fd);
}
Oob_client.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
voidmain()
{
int fd;
int r;
struct sockaddr_in dr;
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd == -1) printf(“%m\n”),exit(-1);
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
dr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r == -1) printf(“%m\n”),exit(-1);
send(fd, “hello”,5,MSG_OOB);
while(1);
close(fd);
}
总结:服务器端程序只接收到一个数据o
1. OOB数据只能一个字符
2. 普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收和发送
3. 一个数据使用MSG_OOB,则最后一个是OOB数据,其他非OOB数据
4. 问题:OOB数据是优先数据,优先体现在什么地方
当有OOB数据时,程序所有都停下来先处理OOB数据
HTTP协议以及应用
HTTP是应用协议
HTTP协议分成:
请求协议(浏览器发给web服务器)
响应协议
请求协议的格式:
请求行(请求方法请求资源协议版本)
请求体(请求头:请求值)
空行
数据(querystring:key=value&key=value)
案例:
向服务器192.168.0.72:80 发送请求,获得index.php页面。
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void main()
{
intfd;
structsockaddr_in dr;
charstrreq[1024];
charbuf[10*1024];
intr;
//建立socket
fd=socket(AF_INET,SOCK_STREAM,0);
//连接服务器192.168.0.72
dr.sin_family=AF_INET;
dr.sin_port=htons(80);
dr.sin_addr.s_addr=inet_addr(“192.168.0.72”);
r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
//构建http请求字符串
sprintf(strreq,
“GET /index.php HTTP/1.1\r\n”//获得index.php页面
“Host: 192.168.0.72:80\r\n”
“User-Agent: Mozilla/5.0\r\n”//User-Agent: 使用的浏览器
“Accept:text/html,image/png\r\n”//Accept: text/支持文本(html)//image/png(支持图片)
“Accept-Language: zh-ch\r\n”//支持的语言 zh-ch 中文
“Accept-Charset: gb2312,utf-8\r\n” //支持的字符集 gb2312 utf-8
“Keep_Alive: 300\r\n”//连接保持300秒
“Connection: keep-alive\r\n”
“\r\n”);
//发送http请求字符串
r=send(fd,strreq,strlen(strreq),0);
//等待服务器响应
printf(“=======================\n”);
while(1)
{
r=recv(fd,buf,1024,0);
if(r<=0) break;
printf(“%s\n”,buf);
}
printf(“=======================\n”);
close(fd);
}
响应请求格式:
响应行(协议版本响应码 响应码的文本描述)
响应体(响应头: 响应值)
空行
数据(普通数据/分块数据)
响应码分类:
1XX 正在处理
2XX 响应成功200
3XX 继续处理
4XX 客户错误
5XX 服务器错误
相关推荐
linux c tcpserver tcpclient udpserver udpclient select:包括tcpserver并发多客户端连接和数据收发;
TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则提供无连接的不可靠数据传输。在Linux网络编程中,开发者需要了解这两种协议的实现原理以及使用方法。 除了基本的协议,Linux...
c++ tcp,udp监听、接收(select,类似ace的reactor)简单封装源码,及测试文件。。适用轻便,可扩展。。若没有安装qt可自己编写makefile自行编译 。。
6、Linux网络编程06——UDP协议编程 7、Linux网络编程07——广播 8、Linux网络编程08——多播 9、Linux网络编程09——TCP编程之客户端 10、Linux网络编程10——TCP编程之服务器 11、Linux网络编程11——tcp、udp迭代...
linux网络编程实例,讲解了一些udp tcp的socket编程,有select的例子
本程序是Linux下基于socket套接字完成的简单网络编程程序,可以接收Tcp、Udp单播、Udp组播消息,并且加入了Select机制和多线程编程,比较简单,适合初学者使用。
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...
网络套接字(socket)编程,深入理解TCP、UDP协议;用select网络框架,写一个简单的C/S程序,可以正常运行。此代码是我用来记录学习网络套接字的过程,代码是在linux操作系统的vim界面下基于C++编写的,如果在VS或者...
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...
第二章 UNIX/Linux 模型...............................................................................................17 2.1 UNIX/Linux 基本结构...........................................................
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...
linux系统的网络开发必学教程;包含课件和例子代码;包含以下内容: 0:课程定位:目标与安排 1:网络编程的概念与模式 2:服务端编程初体验 3:深入浅出 IP 地址 4:尝鲜 select 多路复用 5:基于多路复用的服务端 ...
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...
Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等...
IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06...
很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select函数poll函数epoll函数;最后介绍了UDP协议的服务器编写和本地套接字的...
集成了LINUX下的 TCP UDP 服务器模型,包括 EPOLL ET LT,SELECT,POLL 还包含了LINUX 系统API 资源API 基础库 算法库 客户端开发包等等,与网络相关的功能集成。 与之对应的是WINDOWS网络通信引擎!同时发布
第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 ...
建立TCP、UDP单播、组播 创建服务器端与客户端,服务器端使用select机制接收客户端的请求,并交给线程池处理