首页 > 代码库 > 网络编程整理

网络编程整理

网络编程

1.TCP/UDP特点

1.TCP

1.面向连接(流式套接字 SOCK_STREAM)

2.数据完整安全,可靠,有序

PS:数据完整不丢失,有序 ,数据完整采用CRC循环冗余校验,数据不丢失采用重传机制和超时机制,有序的发送端的拆分编号,接收端排序组合。若在某一段时间内接收端到的为两个一样的数据,则选择丢弃其中的一个。发送者每发送一个tcp段,启动一个定时器,超时没有收到ACK,则重发。而接受者每收到一个tcp段,则发送一个ACK消息。

3. (效率低,传输慢)

2.UDP

1.面向数据报(数据报套接字 SOCK_DGRAM)

2.数据可能会丢失,乱序

3.

流式套接字:

1.数据无界限 (连续发送的多个数据之间无界限)

数据报套接字:

1.数据有明确界限

PS:针对流式套接字存在的数据之间无界限的问题要解决的方法:

场景建立:如果发送一个文件,首先要发送的是文件名,文件大小,文件内容。例如

if(writen(connfd,FILENAME,LEN_FILENAME)<0)

ERR("write failed");

//这儿的文件名为字符串,是属于字符数组,char[]无需转为网络序。

int filesize_nbo = htonl(filesize);

if(writen(connfd,&filesize_nbo,LEN_FILESIZE)<0)

ERR("write failed");

   看网络状况,状况好的情况下,发送的快,则其中间隔较大,若网络不好的情况下,第二次与第一次的间隔较小,都将视为第一次发送的东西。解决办法,规定LEN_FILENAME,LEN_FILESIZE,即使文件名放不满,也要写LEN_FILENAME的这么多的数据过去。这时就要采用writen 保证确实写了如此多的数据过去。保证数据完整性。

2.网络中的数据传输

预备知识:字节序(大小端)

大端(big-endian):高地址>>>>低字节  低地址>>>高字节

小端(little--endian):高地址>>>高字节 低地址>>>低字节

1.单字节数据(char,char[],)

2.多字节数据(short,int,long)

发送之前:HBO(host byteorder) --> NBO(network byteorder)

接收之后:NBO --> HBO

htonl 32bit

ntohs 16bit

ntohl 32bit

3.结构体数据

1.成员要进行字节序转换

2.强制按照1字节补齐(即为不补齐) 加入push pop将结构体限制为push pop的范围之内。

#pragma pack (push)

#pragma pack (1)

struct A{

int a;

char c;

short b;

};

#pragma pack (pop)

struct B{};

4.浮点型数据的发送

整数,小数分开发

5.负数

整数

负数符号单独发

 

3.地址的表示方法(IP + Port)

IP

ipv4(32bit)/ipv6(128bit)

表示法:

字符串  "192.168.2.100"

数字 192<<24 | 168<<16 | 2<<8 | 100;

struct in_addr

{

in_addr_t s_addr; //NBO

};

可以用这种方法赋值:in_addr.s_addr=192<<24|168<<16|2<<8|100

inet_ntop();

inet_pton();

 

PORT

16bit(0-65535)  htons来将转化为网络序

0-1023 //周知端口,特权端口(只有root权限才能使用)

1023-

IPV4地址结构

struct sockaddr_in

{

sin_family_t sin_family //地址族(ipv4 or ipv6)

unsigned short sin_port //NBO 端口

struct in_addr sin_addr //NBO IP

char sin_zero[8] //保留

};

struct sockaddr_in6

{

//...

};

//通用地址结构

struct sockaddr

{

};

 

 

4.TCP编程

SOCK_STREAM(一般专指TCP)

SOCK_DGRAM (一般专指UDP) 

SOCK_RAW(原始套接字,用以访问底层接口)

协议可以使用getprotobyname获取协议值,socket(PF_INET,SOCK_STREAM,0)其中0为协议值。默认为0仅限于TCP UDP,原始套接字编程要使用这个函数获得协议值。

 

1.服务器端

1.创建socket

socket

2.绑定地址

bind

3.监听

listen

while(1)

{

4.接收连接(阻塞)

accept

5.通信(多进程,多线程等模型)

read/write

6.关闭与当前客户端的连接

close/shutdown

}

7.关闭服务器

 

2.客户端

1.创建socket

socket

2.和对方建立连接

connect

3.通信

read/write

4.关闭连接

close/shutdown

 

5.udp编程

1.服务器端

1.创建socket

socket(PF_INET,SOCK_DGRAM,0);

2.绑定地址

bind(sockfd,(struct sockaddr*)&ipv4,sizeof(ipv4));

3.交互

read/write

sendto(sockfd,buf,size,0,const (struct sockaddr*)&peer,sizeof(peer))

recvfrom(sockfd,buf,size,0,struct sockaddr*,socklen_t *len)

4.关闭

close

2.客户端

1.创建socket

socket(PF_INET,SOCK_DGRAM,0);

2.绑定地址(可选)

bind(sockfd,(struct sockaddr*)&ipv4,sizeof(ipv4));

3.交互

read/write

sendto(sockfd,buf,size,0,const (struct sockaddr*)&peer,sizeof(peer))

recvfrom(sockfd,buf,size,0,struct sockaddr*,socklen_t *len)

4.关闭

close

 

数据有明确界限

 

5.udp广播

1.IP地址

网络号 主机号

网络号 子网号 主机号

2.广播地址

1.受限广播地址(所有路由器都不会转发目标为4255的数据)

255.255.255.255

A

B

C

D

E

2.网内广播

子网号,主机号 全为1

3.子网内广播

主机号全为1

192.168.3.10/28的广播地址:192.168.3.15

3.广播编程

1.设置允许发送广播消息

setsockopt(int sockfd,int level,int optname,void *val,size_t len);

int val = 1;

setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val));

2.发送目标地址为广播地址

 

6.udp组播

组播地址:

DIP 224.0.0.1~239.255.255.255

1.发送者

发给一个组播地址

2.接收者(组播编程主要对接受者有限定)

1.加入组

struct ip_mreq{ 

struct in_addr imr_multiaddr;   /* IP multicast address of group */       struct in_addr imr_interface;   /* local IP address of interface */
};

struct ip_mreq mreq;

};

struct ip_mreq mreq;

setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); 

2.接收消息

3.离开组

setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));

 

7.广播组播注意事项:

1.设置缺省网关

route -n

route add default gw 192.168.5.1

2.防火墙

service iptables stop/start/restart/status

 

//=============================================================

1.网络数据库

#include <netdb.h>

 

gethostbyname --> 域名解析 /etc/hosts /etc/resolv.conf

getprotobyname --> /etc/protocols

2.设置网络选项

setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen);

getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen);

 

level:

SOL_SOCKET 通用选项

SO_BROADCAST

SO_LINGER

SO_RCVBUF,SO_SNDBUF

SO_RCVLOWAT , SO_SNDLOWAT 低潮值

SO_REUSEADDR, SO_REUSEPORT

IPPROTO_IP ipv4选项

IP_TTL(time-to-live)

IP_ADDMEMBERSHIP

IP_DROPMEMBERSHIP

IPPROTO_IPV6 ipv6选项

IPPROTO_ICMPV6 icmpv6选项

IPPROTO_TCP tcp选项

3.IO模型

1.阻塞模型

2.非阻塞模型

fcntl(O_NONBLOCK);

if(read(...)<0)

{

if(errno==EGAIN)

{

//do other...

}

}

3.信号驱动IO sigio

4.异步IO

5.IO多路复用

Select:用于监视多个文件模型

 

 

 

网络编程注意事项:

1.优雅关闭问题

1.问题提出

发送者发送完毕,立即关闭连接,并且退出程序,此时接收者可能没有收完。(退出程序,即为原先发送者发送的数据存在缓冲区中,一旦程序退出,缓冲区则会被销毁,自然接受者收到的信息就不完全。)

2.解决

发送者:

1.发送完毕

2.shutdown(sockfd,SHUT_WR); //刷新缓冲  //shutdown可以关闭写 (TCP为全双工模型)

3.read(sockfd,&c,1);  //阻塞等待接受者发送一个消息给发送者,告诉发送者我已经发送完毕。此时有一种情况,若接受者没有发送信息给发送者,则read函数得到的为0,意味着接受者已关闭连接,则发送者也会因此而解除阻塞。

返回0

4.shutdown(sockfd,SHUT_RD);  //关闭读连接

接收者:

1.接收完毕

2.close(sockfd);

 

2.设置地址重用

socket之后,bind之前设置

int val = 1;

setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))

2*msl  个时间断

3.数据传输

多字节

HBO-->NBO

NBO-->HBO

结构体

1字节补齐

多字节成员

...

4.tcp数据界限问题:读取写入固定字节,当然要采用readn writen保证数据的完整性。

 

readn

written

int readn(int fd,void *buf,size_t len)

{

int total = 0,n;

while(total<len)

{

again:

if((n = read(fd,buf+total,len-total))<0)

{

if(errno==EINTR)

goto again;

return -1;

}

else if(n==0)

break;

total += n;

}

return total;

}

 

 

5.MTU(max-transmition-unit),路径MTU

局域网:<1024

外网:<512