Linux 网络编程相关整理

网路编程的基本知识(IP地址\端口)
IP地址 IP地址是用来标示全球计算机地址的一种符号,相当于一个手机的号码,使用这个地址可以访问一台计算机。IP地址是32位长度的的二进制数值,在同一个网络中IP地址是唯一的。IP地址又为逻辑地址,可以通过软件设设置改变主机的IP地址。

端口
端口是为了标示计算机中访问网络的不同程序的编号,每一个程序在访问网络时,都会分配一个标示符。这里的端口并不是实际存在的网卡界限的端口,而是不同程序的逻辑编号,端口是16位的无符号整数,十进制范围为0~65535。低于256的端口为系统保留端口,主要用于进程通信,例如WWW服务用的是80号端口,FTP用的是21号端口。不在这一范围的端口号是自由端口号,在编程时可以被调用。

传输协议
TCP与UDP是两种不同的网络数据传输方式,两者的主要区别是进行数据传输时是否进行连接。
TCP:是一种面向连接的网络传输方式,这种方式可以理解为打电话。主机A呼叫主机B,主机B接收连接后发出确认信息。主机A接收到确认信息后发送信息。主机B完成数据接收后发送完毕信息。这时再关闭数据连接。所以这种连接是可靠的,缺点是传输过程复杂,占用网络资源多。
UDP:是一种无连接的传输方式。它不与对方建立连接,而是直接就把数据包发送过去。因此该协议传输数据安全性差,但效率高。

套接字
套接字的本义是插座,在网络中用来描述计算机中不同程序与其他计算机程序的通信方式。 区分不同应用程序进程间的网络通信和连接,主要有三个参数,即通信的目的IP地址、使用的传输层协议(TCP/UDP)和使用的端口号(套接字=传输层协议+端口号+IP地址)。在编程时就是使用这三个参数来构成一个套接字,这个套接字相当于一个借口,可以进行不同计算机程序的信息传输。
套接字有三种类型:流式套接字( SOCK_STREAM),数据报套接字( SOCK_DGRAM)及原始套接字。
1.流式套接字(SOCK_STREAM):可靠的、面向连接的通讯流。它使用了 TCP,保证了你的数据传输是正确的,并且是顺序的。
2.数据报套接字(SOCK_DGRAM):定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。它使用使用者数据报协议 UDP(User Datagram Protocol)
3.原始套接字:主要用于一些协议的开发,可以进行比较底层的操作。它功能强大,但是没有上面介绍的两种套接字使用方便,一般的程序也涉及不到原始套接字。
套接字相关数据类型

struct sockaddr

{

unsigned short int sa_family; /* 通信地址类型, AF_xxx */

char sa_data[14]; /* 14 个字符长度,用来保存IP地址和端口信息*/

};

struct sockaddr_in

{

         unsigned int sin_family;             /* 通信地址类型 */

         unsigned short int sin_port;       /* 套接字使用的端口号*/

         struct in_addr sin_addr;              /* 需要访问的IP地址*/

         unsigned char sin_zero[8];         /* 未使用字段 */

};

struct in_addr

{

    unsigned long s_addr; /* 保存一个IP地址*/

};

基本转换函数
通常使用的整数有两种数据类型:短型(两个字节)和长型(四个字节)。如果你想将一个短型数据从主机字节顺序转换到网络字节顺序的话,有这样一个函数:它是以“ h”开头的(代表“主机”);紧跟着它的是“ to”,代表“转换到”;然后是“n”代表“网络”;最后是“ s”,代表“短型数据”。 H-to-n-s,就是 htons() 函数。
htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号短型进行操作 4 bytes)
htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符号长型进行操作 8 bytes)
ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符号短型进行操作 4 bytes)
ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符号长型进行操作 8 bytes)
函数: inet_addr(),把一个用数字和点表示 IP地址的字符串转换成一个无符号长整型。
ina.sin_addr.s_addr = inet_addr(“166.111.69.52”);
注意:inet_addr() 返回的地址已经是网络字节顺序了。
函数 inet_ntoa()(“ntoa”代表“ Network to ASCII”):
printf(“%s”, inet_ntoa(ina.sin_addr));
这段代码将会把 struct in_addr里面存储的网络地址以数字 .数字.数字.数字的格式显示出来。
inet_ntoa() 返回一个字符指针,它指向一个定义在函数 inet_ntoa() 中的 static类型字符串。所以每次调用 inet_ntoa(),都会改变最后一次调用 inet_ntoa() 函数时得到的结果。

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
sockfd(套接字): 指向一个打开的套接口描述字
level:(级别): 指定选项代码的类型。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname(选项名): 选项名称
optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
optlen(选项长度) :optval 的大小
返回值:标志打开或关闭某个特征的二进制选项


int select(int maxfd, fd_set *rdset, fd_set *wrset, fd_set *exset, struct timeval *timeout);  
作用:监视多个套接字的状态变化
参数
maxfd是需要监视的最大的文件描述符值+1;
rdset,wrset,exset分别对应于需要检测的可读、可写及异常文件描述符的集合。
struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
fd_set是一组文件描述字(fd)的集合,它用一位来表示一个fd,对于fd_set类型通过下面四个宏来操作:
 FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符
FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。

无连接的套接字通信(socket)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain , int type , int protocol);
domain需要被设置为 “AF_INET”
type参数告诉内核这个 socket是什么类型,“SOCK_STREAM”或是“ SOCK_DGRAM”。
把 protocol设置为 0 。
套接字创建时没有指定名字.客户机用套接字的名字读写它。socket()函数只是简单的返回一个你以后可以使用的套接字描述符。如果发生错误, socket()函数返回 –1 。全局变量 errno将被设置为错误代码。

无连接的套接字通信(sendto)
 #include <sys/types.h>
 #include <sys/socket.h>
 ssize_t sendto(int sockfd,  const void *buf,  size_t len,  int flags,  const struct sockaddr *dest_addr,  socklen_t addrlen);
sockfd是代表你与远程程序连接的套接字描述符。
buf是一个指针,指向你需要发送字符串的地址。
len是你想发送信息的长度。
flags发送标记。一般都设为 0。(你可以查看 send的 man pages来获得其他的参数值并且明白各个参数所代表的含义)
dest_addr是一个指向 struct sockaddr结构的指针,里面包含了远程主机的 IP地址和端口数据。
addrlen指出了 struct sockaddr在内存中的大小 sizeof(struct sockaddr)。sendto()返回它所真正发送的字节数(它所真正发送的字节数可能小于你所给它的数据的字节数)。当它发生错误的时候,也是返回 –1 ,同时全局变量 errno存储了错误代码。

#include <sys/types.h>
#include <sys/socket.h>
size_t recvfrom(int sockfd,  void *buf,  size_t len,   int flags,  struct sockaddr *src_addr,  socklen_t *addrlen);
sockfd是你要读取数据的套接字描述符。
buf是一个指针,指向你能存储数据的内存缓存区域。
len是缓存区的最大尺寸。
flags是 recv() 函数的一个标志,一般都为 0(具体的其他数值和含义请参考 recv()的 man pages)。
src_addr是一个本地指针,指向一个 struct sockaddr的结构(里面存有源 IP地址和端口数).
addrlen是一个指向一个 socklen_t型数据的指针,它的大小应该是 sizeof( struct sockaddr).当函数返回的时候, addrlen指向的数据是 src_addr指向的 struct sockaddr的实际大小.
recvfrom() 返回它接收到的字节数,如果发生了错误,它就返回–1 ,全局变量 errno存储了错误代码.

面向连接的套接字通信(bind)
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd , struct sockaddr *my_addr , socklen_t addrlen) ;
参数说明:
sockfd是由socket()函数返回的套接字描述符。
my_addr是一个指向 struct sockaddr的指针。
addrlen可以设置为 sizeof(struct sockaddr)。
bind()函数可以帮助你指定一个套接字使用的端口。
当你需要进行端口监听 listen()操作,等待接受一个连入请求的时候,一般都需要经过这一步。如果你只是想进行连接一台服务器,也就是进行 connect() 操作的时候,这一步并不是必须的。
bind()函数调用错误的时候,返回–1作为错误发生的标志。errno的值为错误代码。

面向连接的套接字通信(listen)
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd是一个套接字描述符,由 socket()系统调用获得。
backlog是未经过处理的连接请求队列可以容纳的最大数目。 每一个连入请求都要进入一个连入请求队列,等待 listen的程序调用 accept()函数来接受这个连接。当系统还没有调用 accept()函数的时候,如果有很多连接,那么本地能够等待的最大数目就是 backlog的数值。
listen()如果返回 –1 ,那么说明在 listen()的执行过程中发生了错误。全局变量 errno中存储了错误代码。
在 listen()函数调用之前,需要使用 bind() 函数来指定使用本地的哪一个端口数值。
如果你想在一个端口上接受外来的连接请求的话,那么函数的调用顺序为:
socket() ;
bind() ;
listen() ;
/* 在这里调用 accept()函数 */

面向连接的套接字通信(accept)
#include <sys/socket.h>
int accept(int sockfd, void *addr, socklen_t *addrlen);
sockfd是正在 listen() 的一个套接字描述符。
addr一般是一个指向 struct sockaddr_in结构的指针;里面存储着远程连接过来的计算机的信息(计算机的IP地址和端口)。
addrlen是一个本地的整型数值,在它的地址传给 accept()前它的值应该是 sizeof(struct sockaddr_in);accept()不会在 addr中存储多余addrlen bytes大小的数据。如果accept()函数在 addr中存储的数据量不足 addrlen,则 accept()函数会改变 addrlen的值来反应这个情况。
如果调用 accept()失败的话,accept()函数会返回 –1来表明调用失败,同时全局变量 errno将会存储错误代码。
当调用accept()的时候,大致过程是下面这样的:有人尝试调用 connect()来连接你的机器上的某个端口(当然是你已经在 listen()的)。他的连接将被 listen加入等待队列等待 accept()函数的调用。 你调用 accept()函数,告诉他你准备连接。
accept()函数返回一个新的套接字描述符,这个描述符就代表了这个连接。这时候你有了两个套接字描述符,返回给你的那个就是和远程计算机的连接,而第一个套接字描述符仍然在你的机器上原来的那个端口上listen()。这时候你所得到的那个新的套接字描述符就可以进行 send()操作和 recv()操作了。

面向连接的套接字通信(connet)
#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
sockfd :套接字文件描述符,由 socket()函数返回的。
serv_addr是一个存储远程计算机的 IP地址和端口信息的结构。
addrlen应该是 sizeof(struct sockaddr)。
一定要检测 connect()的返回值:如果发生了错误(比如无法连接到远程主机,或是远程主机的指定端口无法进行连接等)它将会返回错误值 –1 。全局变量 errno将会存储错误代码。

面向连接的套接字通信(recv)
#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, socklen_t len, unsigned int flags);
sockfd是你要读取数据的套接字描述符。
buf是一个指针,指向你能存储数据的内存缓存区域。
len是缓存区的最大尺寸。
flags是 recv() 函数的一个标志,一般都为 0(具体的其他数值和含义请参考 recv()的 man pages)。
recv() 返回它所真正收到的数据的长度。(也就是存到 buf中数据的长度)。如果返回 –1则代表发生了错误(比如网络以外中断、对方关闭了套接字连接等),全局变量 errno里面存储了错误代码。

面向连接的套接字通信(send)
#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, socklen_t len, int flags);
sockfd是代表你与远程程序连接的套接字描述符。
msg是一个指针,指向你想发送的信息的地址。
len是你想发送信息的长度。
flags发送标记。一般都设为 0。
send()函数在调用后会返回它真正发送数据的长度。
注意:send() 所发送的数据可能少于你给它的参数所指定的长度!因为如果你给 send()的参数中包含的数据的长度远远大于 send()所能一次发送的数据,则 send()函数只发送它所能发送的最大数据长度,然后它相信你会把剩下的数据再次调用它来进行第二次发送。所以,记住如果 send()函数的返回值小于 len的话,则你需要再次发送剩下的数据。幸运的是,如果包足够小(小于 1K),那么 send()一般都会一次发送光的。
send()函数如果发生错误,则返回 –1 ,错误代码存储在全局变量 errno中。

单播、组播、广播
当前的网络中有三种通讯模式:单播、广播、组播(多播),其中的组播出现时间最晚但同时具备单播和广播的优点,最具有发展前景。
单播:主机之间“一对一”的通讯模式。
广播:主机之间“一对所有”的通讯模式。
组播:主机之间“一对一组”的通讯模式。
主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要(未加入组)的主机的其他通讯。

组播
组播IP地址用于标识一个IP组播组。IANA(internet assigned number authority)把D类地址空间分配给IP组播,其范围是从224.0.0.0~239.255.255.255,可分为以下三类:  
保留——224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用。
用户组播地址——224.0.1.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效。
本地管理组——239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。常用的预留组播地址列表如下:
224.0.0.0 基准地址(保留)
224.0.0.1 所有主机的地址
224.0.0.2 所有组播路由器的地址
.. ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值