UNIX( linux) 网络编程

UNIX( linux) 网络编程bzero( *arg, sizeof(arg) )将整个数据结构智位0, memset(*arg, 0, sizeof(arg) ),意义一样,但记忆更麻烦。

fputs是一个函数,具有的功能是向指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’)。成功写入一个字符串后,文件的位置指针会自动后移,函数返回值为非负整数;否则返回EOF(符号常量,其值为-1)。

int fputs(const char *str, FILE *stream);
返回值:该函数返回一个非负值,如果发生错误则返回 EOF(-1)。
(1)str:这是一个数组,包含了要写入的以空字符终止的字符序列。

(2)sstream:指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流

用函数,因为要有函数调用,增加了执行时的开销,效率不如宏,但是可代码重用。用宏虽然没有函数调用,但是因为在使用这个宏的地方,代码会被展开编译,增加了程序文件的大小。总起来说,参数和宏是一个用空间换时间还是用时间换空间的抉择。

getaddrinfo(), gethostbyname()。

端口划分(1023 known,1024-49151register 49152-65535dynamic

UNIX( linux) 网络编程UNIX( linux) 网络编程UNIX( linux) 网络编程

UNIX( linux) 网络编程

SYN标识建立连接        Synchronize
FIN表示响应
ACK表示响应    ACK (ACKnowledge Character) 
PSH表示有DATA数据传输

RST表示连接重置Reset

netdump

UNIX( linux) 网络编程


IPv6的报文头部结构如图:
UNIX( linux) 网络编程
版本号 表示协议版本.值为6
流量等级 主要用于QoS
流标签 用来标识同一个流里面的报文
载荷长度 表明该IPv6包头部后包含的字节数,包含扩展头部
下一报头 该字段用来指明报头后接的报文头部的类型,若存在扩展头,表示第一个扩展头的类型,否则表示其上层协议的类型,它是IPv6各种功能的核心实现方法
跳数限制 该字段类似于IPv4中的TTL,每次转发跳数减一,该字段达到0时包将会被丢弃
源地址 标识该报文的来源地址
目的地址 标识该报文的目的地址

inet6 telnet ping


struct in_addr {

    in_addr_t a_addr;//32bit

}

struct sockaddr_in {

     uint8_t    sin_len;

    sa_family_t    sin_family;

    in_port_t        sin_port;

    struct   in_addr  sin_addr;

    char                sin_zero[8];

}

void func(主体, 约束, 客体, 约束);

结构体使用地址

通用套接字结构

struct sockaddr_in {

    uint8_t         sa_len;

    sa_family_t    sa_family;

     char                sa_data[14];

}

casting (struct sockaddr*)&xx.



从进程到内核传递套截至结构的函数有: bind, connect, sendto


从内核传到进程的有: accept, recvfrom, getsockname, getpeername,    

在网络编程中, 值-结果参数最常见的例子是所返回套接字地址结构的长度,


网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian(大端)排序方式。

为了进行转换 bsd socket提供了转换的函数 有下面四个
  htons把unsigned short类型从主机序转换到网络序
  htonl 把unsigned long类型从主机序转换到网络序
  ntohs 把unsigned short类型从网络序转换到主机序
  ntohl 把unsigned long类型从网络序转换到主机序
  在使用little endian的系统中 这些函数会把字节序进行转换
  在使用big endian类型的系统中 这些函数会定义成空宏
  同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.

1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
  2、不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。
  处理器 操作系统 字节排序
  Alpha 全部 Little endian
  HP-PA NT Little endian
  HP-PA UNIX Big endian

  Intelx86 全部 Little endian


字节操纵函数://<strings.h>
源自4.3bsd套接字提供:
void bzero(void *s, size_t n);
void bcopy(const void *src, void *dest, size_t n);
int bcmp(const void *s1, const void *s2, size_t n);

源自ANSC C提供://<string.h>
void *memset(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n);

int memcmp(const void *s1, const void *s2, size_t n);


地址转换函数://<arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp); /返回:有效为1,无效为0

in_addr_t inet_addr(const char *cp);//有效返回地址,否则INADDR_NONE(-1)

in_addr_t inet_network(const char *cp);//返回地址


新的函数,兼容v4和v6: int inet_pton(int family, const char* *strptr, void *addrptr)//返回:成功为1,无效为0, 出错-1

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)//返回:成功为结果的指针,出错为NULL #define NULL ((void*)0)


另外:AF_INET 为 2, AF_INET 为 10;

struct sockaddr_in6 addr6;  addr6.sin_addr6;

ssize_t  read(int fd, void* buf, size_t count); 

UNIX( linux) 网络编程
ssize_t readline(int fd, void *usrbuf, size_t maxlen),
ssize_t write(int fd, const void *buf, size_t count);

a) >0,表示成功读取的字节数,如果小于n,说明中间遇到了EOF;
b)==0 表示一开始读取就遇到EOF;
c) -1 表示错误(这里的errno绝对不是EINTR)。

int socket(int domain, int type, int protocol);//返回:成功返回一个小整数例如3,出错返回-1。socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。

对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。


bind(int sockfd, int maxNums)//将一本地地址与一套接口捆绑。本函数适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用。当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。

面向连接的网络应用程序分为客户端和服务器端。服务器端的执行流程一般为4步,客户端程序相对简单,一般需要两个步骤。

服务器端执行流程4步如下:
(1)调用socket函数,建立一个套接字,该套接字用于接下来的网络通信。
(2)调用bind函数,将该套接字绑定到一个地址,并制定一个端口号,
(3)调用listen函数,使用该套接字监听连接请求
(4)当请求来到时,调用accept函数复制该套接字处理请求
客户端执行流程2步如下:
(1)调用socket函数,创建一个套接字
(2)调用connect函数使用该套接字与服务器进行连接

listen

int connect(SOCKET s, const struct sockaddr * name, int namelen);//connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址. 结构sockaddr请参考bind(). 参数addrlen 为sockaddr 的结构长度.返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.

 accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//如果没有错误产生,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,应用程序可通过调用WSAGetLastError()来获得特定的错误代码。

addrlen所指的整形数初始时包含addr所指地址空间的大小,在返回时它包含实际返回地址的字节长度。


setsockopt()#include <sys/socket.h>
int setsockopt(int s,int level,int optname,
const char *optval,int optlen);
s:标识一个套接字的描述符。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区长度。

send 有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性;另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。

recv



I/O模型

阻塞式IOUNIX( linux) 网络编程

非阻塞     ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen)

UNIX( linux) 网络编程

I/O复用(select,poll)

UNIX( linux) 网络编程

信号驱动UNIX( linux) 网络编程


UNIX( linux) 网络编程


异步

5种 I/O 模型的比较

UNIX( linux) 网络编程


#include <sys/select.h>
int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。

timeout:select()最多等待时间,对阻塞操作则为NULL。




#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。


#include<unistd.h>  
#include<fcntl.h>  
int fcntl(int fd, int cmd);  
int fcntl(int fd, int cmd, long arg);  

int fcntl(int fd, int cmd ,struct flock* lock);  


关闭非阻塞式I/O:


[cpp] view plain copy
#include <stdio.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <errno.h>  
  
//取消非阻塞    
static void setblocking(int sockfd) {    
    int flag &= ~O_NONBLOCK;    
    if (fcntl(sockfd, F_SETFL, flag) < 0) {    
        perror("fcntl F_SETFL fail");    
    }    

}  


ioctl是设备驱动程序中对设备的I/O通道进行管理的函数 。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下: 
int ioctl(int fd, ind cmd, …);