写在前面:总结一下几个常用的SOCKET API。
正文:
1、socket():
函数原型:int socket(int domain,int type, int protocol);
函数作用:用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数
参数:domain:协议族/域,通常AF_INET(IPv4)、AF_INET6(IPv6)
type:是套接口类型,主要SOCK_STREAM(TCP协议)、SOCK_DGRAM(UDP协议)
protocol:一般为0
返回:成功时返回非负整数。
2、(1)、htons():
函数原型:uint16_t htons(uint16_t hostshort);
函数作用:htons是将整型变量从主机字节顺序转变成网络字节顺序,
就是整数在地址空间存储方式变为高位字节存放在内存的低地址处,
网络字节顺序采用big-endian排序方式。
htonl():
函数原型:uint32_t htonl(uint32_t hostlong);
函数作用:本函数将一个32位数从主机字节顺序转换成网络字节顺序。
(2)、inet_ntoa():
函数原型:char *inet_ntoa(struct in_addr in);
函数作用:将IP地址,转换为点分十进制的字符串格式;
注意为大端模式即0x78对应120,0x56对应86.......
(3)、inet_pton()
原型: int inet_pton(int af, const char *src, void *dst);
函数作用:将点分十进制的字符串格式的IP地址,转换成整数格式。
参数:af : AF_INET代表IPv4; AF_INET6 代表IPv6;
src:字符串格式点分十进制的IP地址;
dst:转换后的IP地址。
3、inet_addr():
原型:in_addr_t inet_addr(const char *cp);
函数作用:inet_addr()的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)。
参数:字符串,一个点分十进制的IP地址。
4、select 函数以及 select相关函数:
函数原型: int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
函数作用:它能够监视我们需要监视的文件描述符的变化情况——读写或是异常
两个结构体:struct fd_set 可以理解为一个集合,这个集合中存放的是文件描述符,
fd_set集合可以通过一些宏由人为来操作,比如清空集合:FD_ZERO(fd_set*),
将一个给定的文件描述符加入集合之中 FD_SET(int, fd_set*),
将一个给定的文件描述符从集合中删除 FD_CLR(int, fd_set*),
FD_ISSET(int, fd_set*):检查集合中指定的文件描述符是否可以读写 ,
即检查 int所对应的套接字是否在这个集合当中,
select函数后将更新这个集合,把其中不可读的套节字去掉 。
struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,
一个是秒数,另一个毫秒数,即select函数是非阻塞的,超过这个设定的时间后可退出。
这个时间设置的用法:
第一:若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,
一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二:若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,
都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三:timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,
超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
参数解释:
readfds: (可选)指针,指向一组等待可读性检查的套接口;
writefds: (可选)指针,指向一组等待可写性检查的套接口;
exceptfds:(可选)指针,指向一组等待错误检查的套接口;
timeout: 本函数最多等待时间,对阻塞操作则为NULL。
5、bind函数:
函数原型:bind( SOCKET sockaddr, const struct sockaddr my_addr,int addrlen);
函数作用:套接字绑定到一个地址,并制定一个端口号。
将套接字绑定一个IP地址和端口号,因为这两个元素可以在网络环境中唯一地址表示一个进程。
6、listen函数:
函数原型:int listen(SOCKET sockfd, int backlog);
函数作用:listen函数使用主动连接套接字变为被连接套接口,使得一个进程可以接受其它进程的请求,
从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
listen函数一般在调用bind之后-调用accept之前调用。
7、accept函数:
函数原型: int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen)。
函数作用:在服务器端,socket()返回的套接字用于监听(listen)和接受(accept)客户端的连接请求。
这个套接字不能用于与客户端之间发送和接收数据。
accept()接受一个客户端的连接请求,并返回一个新的套接字。
所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。
与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数据来完成的。
accept()函数仅被TCP类型的服务器程序调用,从已完成连接队列返回下一个建立成功的连接,
如果已完成连接队列为空,线程进入阻塞态睡眠状态。成功时返回套接字描述符,错误时返回-1。
如果accpet()执行成功,返回由内核自动生成的一个全新socket描述符,
用它引用与客户端的TCP连接。通常我们把accept()第一个参数成为监听套接字(listening socket),
把accept()功能返回值成为已连接套接字(connected socket)。
一个服务器通常只有1个监听套接字,监听客户端的连接请求;
服务器内核为每一个客户端的TCP连接维护1个已连接套接字,用它实现数据双向通信。
8、setsockopt()函数:
函数原型:int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
函数作用:设置与某个套接字关联的选项,例如:recv等函数默认为阻塞模式(block),即直到有数据到来之前函数不会返回,
而我们有时则需要一种超时机制使其在一定时间后返回而不管是否有数据到来,这里我们就会用到setsockopt()函数。
参数:
sock:将要被设置选项的套接字。
level:选项所在的协议层,可以取三种值:
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项.
optname:指定控制的方式,根据level的不同,可以由如下配置方式:
选项名称 说明 数据类型
========================================================================
SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
========================================================================
IPPROTO_IP
------------------------------------------------------------------------
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
========================================================================
IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
========================================================================
optval:根据optname的不同,这个参数也不一样,例如当optname为 SO_RCVTIMEO 超时接受时,optval可以为
struct timeval
{
time_t tv_sec;
time_t tv_usec;
};
这种类型的结构体,表示满足一定时间后退出。
optlen:optval的长度。
9、fcntl()函数:
函数原型:int fcntl(int fd, int cmd, long arg);
函数作用:可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性。
参数:fd,socket函数返回的描述符;
cmd,这里先用两个cmd举例,
F_GETFL:获取文件打开方式的标志。
F_SETF :设置文件打开方式为第三个参数arg指定方式
通常用这个函数来设置阻塞性质,举例:
int flags = fcntl(socket, F_GETFL, 0); //将文件描述符的标志保存到flags中
/* 设置为非阻塞*/
if (fcntl(socket_descriptor, F_SETFL, flags | O_NONBLOCK) < 0)
{
/* Handle error */
}
/* 设置为阻塞 */
if ((flags = fcntl(sock_descriptor, F_SETFL, 0)) < 0)
{
/* Handle error */
}
总结:
TCP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;
TCP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
UDP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收数据,用函数recvfrom();
5、关闭网络连接;
UDP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置对方的IP地址和端口等属性;
5、发送数据,用函数sendto();
6、关闭网络连接;