linux socket函数参数_linux socket函数详解_socket()函数(2)
注意并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。
当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。
(4)绑定地址和端口(bind)
函数:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
正如上面所说bind()函数为创建好的socket绑定一个地址族。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。函数的三个参数分别为:
* sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
* addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
ipv6对应的是:struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
Unix域对应的是:
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
* addrlen:对应的是地址的长度。
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
(5)主机字节序与网络字节序转换(htonl)
主机字节序就是我们平常说的大端和小端模式。不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式是低地址存放高位字节(内存起始处存放大端),小端方式是低地址存放低位字节(内存起始处存放小端)。采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。x86平台使用小端模式的主机字节序。
另外注意,内存地址是用来标记每个字节的而不是位,所以内存里面大端小端也是以字节而不是位为单位的(前面描述“大端“、”小端”的时候却以位序而非字节序,这一点需要明辨,不要混淆)。大端序规定高位字节在存储时放在低地址上,在传输时高位字节放在流的开始。假设奔腾的机器,cpu为32位,采用little endian方式,那么表示1这个int类型整数的时候,假设它在数值上是十六进制的"00000001",那么存放在内存中却是由低位到高位依次存放的,由低到高地址依次为:"01"、"00"、"00"、"00"(也就是说小端方式存放在内存中的时候,是按照含有最低位的字节存放在低地址,注意是字节,在内存中“位”没有地址,所以没有大端小端一说)。
注释: 本函数将一个32位数从主机字节顺序转换成网络字节顺序。字节集转换 = 读内存字节集 (-1, 人物武器 + 1232, 12) ' 注意这个变量字节集转换 它的类型是字节集 -1代表自身进程,人物武器+1232就是人物武器+4d0,4d0转成10进制就是1232,12是长度,这句意思就是读取人物当前称号的4d0偏移下的数据保存到字节集转换这个变量里。对未来的挑战,首先,要在思想观念上的转换,在网络空间里企业必须仔细思考自己提供的是什么、如何提供和靠什么来提供,然后必须决定哪种策略和决心最有利于自己目标的实现,也就是要艺术性地把顾客对自己产品的忠诚度转化到网络上来,而转化的关键就是破解顾客忠诚建立的基础是内容牤场景牤基础设施,还是三者因素都起作用牽其次,要动态地去观察网络市场,哪些行业领域正向网络挺进、谁在网络上推销产品和服务牽哪些竞争对手正通过网络营销增加其商品的附加值牽第三,要确立好自己产品的网络营销策略,是创造具有特殊吸引力的内容取胜,还是依托速度发展一种着重于新的市场交易场景,或是单纯开发一种基础设施战略。
下面是几个字节顺序转换函数:
htonl():把32位值从主机字节序转换成网络字节序
htons():把16位值从主机字节序转换成网络字节序
ntohl():把32位值从网络字节序转换成主机字节序
ntohs():把16位值从网络字节序转换成主机字节序
(6)服务端侦听(listen)和客户端连接(connect)
函数:
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值。 webservice也一样,webservice客户端要调用一个webservice服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以,webservice务器端首先要通过一个wsdl文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。webservice也一样,webservice客户端要调用一个webservice服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以,webservice务器端首先要通过一个wsdl文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。
(7)接收请求(accept)
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)。int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)。int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)。
smtp是个请求/响应协议,它监听25号端口,用于接收用户的mail请求,并与远端mail服务器建立smtp连接。对于一个服务器套接字调用listen()成员函数进行监听,对于一个客户套接字调用connect()成员函数来请求连接。使用socket进行client/server程序设计的一般连接过程是这样的:server端listen(监听)某个端口是否有连接请求,client端向server端发出connect(连接)请求,server端向client端发回accept(接受)消息。
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
注意accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
(8)数据传输(send/recv)
万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现网络中不同进程之间的通信!网络I/O操作有下面几组:
* read()/write()
* send()/recv()
* readv()/writev()
* sendmsg()/recvmsg()
* sendto()/recvfrom()
我推荐使用sendmsg()/recvmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数。它们的声明如下:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。
热血丹心