`
jishublog
  • 浏览: 868995 次
文章分类
社区版块
存档分类
最新评论

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_tinet_lnaof(structin_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 tcpserver tcpclient udpserver udpclient select并发编程

    linux c tcpserver tcpclient udpserver udpclient select:包括tcpserver并发多客户端连接和数据收发;

    Linux网络编程,包括tcp/upd,select/poll/epoll/pdf

    TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则提供无连接的不可靠数据传输。在Linux网络编程中,开发者需要了解这两种协议的实现原理以及使用方法。 除了基本的协议,Linux...

    linux c++ tcp,udp封装

    c++ tcp,udp监听、接收(select,类似ace的reactor)简单封装源码,及测试文件。。适用轻便,可扩展。。若没有安装qt可自己编写makefile自行编译 。。

    linux C语言 网络编程教程及源码

    6、Linux网络编程06——UDP协议编程 7、Linux网络编程07——广播 8、Linux网络编程08——多播 9、Linux网络编程09——TCP编程之客户端 10、Linux网络编程10——TCP编程之服务器 11、Linux网络编程11——tcp、udp迭代...

    linux-socket-c.rar_linux socket tcp_linux 网络编程_linux网络编程_socket

    linux网络编程实例,讲解了一些udp tcp的socket编程,有select的例子

    Linux下基于socket的网络编程(包含多线程和Select机制)

    本程序是Linux下基于socket套接字完成的简单网络编程程序,可以接收Tcp、Udp单播、Udp组播消息,并且加入了Select机制和多线程编程,比较简单,适合初学者使用。

    Linux网络编程 视频 教程

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...

    TCP协议socke套接字(服务端&客户端)网络协议配置代码

    网络套接字(socket)编程,深入理解TCP、UDP协议;用select网络框架,写一个简单的C/S程序,可以正常运行。此代码是我用来记录学习网络套接字的过程,代码是在linux操作系统的vim界面下基于C++编写的,如果在VS或者...

    C++教程网《Linux网络编程》视频百度云地址

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...

    Linux网络编程.pdf socket tcp udp

    第二章 UNIX/Linux 模型...............................................................................................17 2.1 UNIX/Linux 基本结构...........................................................

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...

    c++教程网的linux网络编程视频下载

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...

    Linux网络开发必学教程(包含课件和例子代码).rar

    linux系统的网络开发必学教程;包含课件和例子代码;包含以下内容: 0:课程定位:目标与安排 1:网络编程的概念与模式 2:服务端编程初体验 3:深入浅出 IP 地址 4:尝鲜 select 多路复用 5:基于多路复用的服务端 ...

    C++教程网视频:linux网络编程

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念...

    2018年C++教程网的linux网络编程视频共41集百度云下载链接.rar

    Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等...

    linux网络编程

    IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06...

    Linux网络编程socket编程学习

    很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select函数poll函数epoll函数;最后介绍了UDP协议的服务器编写和本地套接字的...

    LINUX 网络通信引擎中间件

    集成了LINUX下的 TCP UDP 服务器模型,包括 EPOLL ET LT,SELECT,POLL 还包含了LINUX 系统API 资源API 基础库 算法库 客户端开发包等等,与网络相关的功能集成。 与之对应的是WINDOWS网络通信引擎!同时发布

    linux网络编程-宋敬彬-part1

    第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 ...

    socket编程 使用select与线程池

    建立TCP、UDP单播、组播 创建服务器端与客户端,服务器端使用select机制接收客户端的请求,并交给线程池处理

Global site tag (gtag.js) - Google Analytics