A 套接字:socket用于本机的两个程序通讯或者两个在不同机器上的进程通讯。
1.创建套接字
int socket(int domian, int type, int protocol);
头文件:#include<sys/types.h>
#include<sys/socket.h>
domian : 域,包括AF_INET(因特网),AF_UNIX(Unix本机),AF_ISO,AF_NS,AF_IPX,AF_APPLETALK。
主要使用AF_INET, AF_UNIX.
可以使用AF_UNIX创建socket文件,来完成两个进程的本地通讯。此时两个进程要约定同一个socket文件通讯,
正如使用管道一样(可以看到在那个目录下生成了socket文件)。
type :类型,SOCK_STREAM(TCP)和SOCK_DGRAM(UDP)。(好像至少在windows中还有SOCK_RAW,原始套接字,比如用于检测网络)
protocol:协议,一般为默认0。
socket返回一个文件描述符,当这个套接字和通信线路另一端的套接字连接好之后,可以用read和write进行收发。用close进行关闭。
2.绑定套接字(命名套接字)
int bind(int socket, const struct sockaddr * address, size_t address_len);
#include<sys/socket.h>
地址:对于AF_UNIX的地址是sockaddr_un。
其结构为:struct sockaddr_un
{
sa_family_t sun_family; /*AF_UNIX*/
char sun_path[]; /*pathname*/
};
对于AF_INET的地址是sockaddr_in。
其结构为: struct sockaddr_in
{
short int sin_family; /*AF_INET*/
unsigned short int sin_port; /*port number*/
struct in_addr sin_addr; /*Internet address*/
};
其中struct in_addr{
unsigned long int s_addr;
}
这是一个32位的二进制数字。
注意:
(1)此时需要进行字节序的装换,分别是htonl,htons,ntohl,ntohs.
如端口号的赋值:address.sin_port=htons(9734);
(2)用点分十进制的方式进行IP地址赋值的时候要用到inet_addr();
如address.sin_addr.s_addr=inet_addr("127.0.0.1").
用了inet_addr产生的是网络字节序,所以不需要再次使用。
(3)IP地址如果是INADDR_ANY,表示对本机的所有ip的某个端口进行监听。(一台主机往往有多个ip)
如address.sin_addr.s_addr=htonl(INADDR_ANY);
bind成功返回0,失败返回-1.
3. 创建套接字队列:
int listen(int socket, int backlog);
头文件:#include<sys/socket.h>
功能:创建一个套接字队列,用来接收请求。
backlog是队列的长度,表示最多容纳多少个请求。
4. 接收请求:
int accept(int socket, struct sockaddr *address, size_t * address_len)
头文件: #include<sys/socket.h>
accept是阻塞形式的,可以通过改变套接字属性来改变这一属性。它会在listen队列中,取第一个请求后返回。
address:是客户端的address。如果不关心客户的地址,可以设置为空指针。
如果成功会返回新的套接字连接符。
有了新的套接字连接符之后,可以像操作文件一样操作套接字。套接字是双工的。read和write都可以。
5. 请求连接:
int connect(int socket, const struct sockaddr *address, size_t address_len);
头文件:#include<sys/socket.h>
socket是本地的socket,地址是外地地址。
connect成功会返回0,失败则返回-1。
connect会被阻塞一定的时间。
6. 关闭套接字:
close(int socketfd);
7. 改变套接字属性:
int setsockopt(int socket,int level, int option_name, const void * option_value, size_t option_len);
如果想在Socket层上改变属性,必须设置level为SOL_SOCKET。
B 网络信息:
1. 获取主机名:
int gethostname(char *name ,int namelength);
获取本机名称放入name中,成功返回0,失败返回-1。
2. 获取某台主机的IP地址:
struct hostent * gethostbyname(const char *name);
#include <netdb.h>
查DNS或者本机的DNS表。
hostent结构体:
struct hostent
{
char *h_name; /* name of the host*/
char **h_aliases; /*list of aliases*/
int h_addrtype; /*address type*/
int h_length; /*length in bytes of the address*/
char **h_addr_list; /*list of address (network order)*/
}
3. 打印套接字地址:
char * inet_ntoa(struct in_addr in);
将结构体套接字用点分十进制的方式打印出来。
C select 系统调用:
1.fd_set 套接字文件集。
void FD_ZERO(fd_set * fdset); //将集合置空
void FD_CLR(int fd, fd_set *fdset); //将某个文件从集合中删除
viod FD_SET(int fd, fd_set *fdset); //将某个文件加入到集合中
int FD_ISSET(int fd, fd_set *fdset); //测试某个文件是否在集合中,如果是返回非0值。
fd_set中能够容纳的元素的个数由FD_SETSIZE值来限定。
2.timeval, 防止select无限阻塞。
struct timeval
{
time_t tv_sec;
long tv_usec;
}
3.select定义:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
readfds处于读操作就绪状态,writefds集合里有描述符处于写操作就绪状态,errorfds集合里有描述符遇到一个错误条件。
poll系统调用:
1.功能: 与select相似。从一组事件中响应一个事件。
2.pollfd:用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,
系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,
在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,
select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把
socket描述符重新加入到待检测的集合中;
typedef struct pollfd
{
int fd; //文件号
short events; //对文件描述符上感兴趣的事件
short revents; //文件描述符实际发生的事件
}pollfd_t
常用事件是:POLLIN、POLLOUT、POLLERR
如:设置感兴趣的事件:fds[nIndex].events=POLLIN | POLLOUT | POLLERR;
检测是哪个事件发生了:f((fds[nIndex].revents & POLLIN) == POLLIN){...};
3.int poll(struct pollfd fds[], nfds_t nfds, int timeout);
#include<poll.h>
fds[] :文件描述符的集合
nfds :文件描述符个数
timeout:超时,如果设置为0会立即返回,如果设置为INFIM,则一直阻塞。
返回
>0 : 发生事件的文件总数
=0 :超时
<0 : 错误发生