基本套接字API回顾
通常我们要做的第一件事就是获取连接的套接字。可以用socket系统调用来实现。
#inclue<sys/socket.h>
int socket(int domain,int type,int protocol);
返回值:成功时返回套接字描述符,失败时返回-
domain是一个常量,用来表示所期望的通信域。最常见的两个域是AF_INET(也就是因特网)和AF_LOCAL(或AF_UNIX).
参数type说明了要创建的套接字类型。
§ SOCK_STREAM——这些套接字提供一个可靠的,全双工,面向连接的字节流。在TCP/IP中就是TCP。
§ SOCK_DGRAM——这些套接字提供一个不可靠,尽力而为的数据报服务。在TCP/IP中就是UDP。
§ SOCK_RAW——这些套接字允许对IP层的某些数据报进行访问。可用于一些特殊的目的,比如监听ICMP报文。
protocol字段说明了应该在套接字上使用那种协议。对于TCP/IP来说,这个字段通常都
由套接字类型隐式说明,参数被设置为零。在某些情况下,比如对原始套接字来说,有几种可能的协议,就要制定希望使用的协议。
对最简单的TCP客户端来说,与对等实体建立会话是需要使用的其他套接字API只有connect一种,connect是用来建立连接的。
#include<sys/socket.h>
int connect(SOCKET s,const struct sockaddr *peer,int peer_len);
返回:成功是返回0,失败时返回-1
参数s是socket调用返回的套接字描述符。参数peer指向一个地址结构,这个结构中装载了期望的对等实体地址和其他一些信息。对AF_INET域来说就是一个sockaddr_in结构。参数peer_len是peer所指结构的长度。
一旦连接建立起来,就可以传送数据了。
#include<sys/socket.h>
int recv(SOCKET s, void *buf, size_t len,int flags);
int send(SOCKET s,const void *buf,size_t len,int flags);
返回:如成功则为读入或写出的字节数,如出错则为-1
参数flags的取值通常与系统有关,其值为0或下面列出的一个或多个常值的逻辑或。
l MSG_DONTROUTE 本标志告诉内核目的主机在某个直接连接的本地网络上,因而无需执行路由表查找。
l MSG_DONTWAIT 本标志在无需打开相应套接字的非阻塞标志的前提下,把单个I/O操作临时指定为非阻塞,接着执行I/O操作,然后关闭非阻塞标志。
l MSG_OOB 对于send,本标志指明即将发送带外数据。对于recv本标志指明即将读入的是带外数据而不是普通数据。
l MSG_PEEK 本标志适用于recv和recvfrom,它允许我们查看已可读取的数据,而且系统不再recv或recvfrom返回后丢弃这些数据。
l MSG_WAITALL 它告诉内核不要在尚未读入请求数目(len)的字节之前让一个读操作返回。(注:如果发生下列情况之一:(a)捕获一个信号 (b)连接被终止 (c)套接字发送一个错误,相应的读函数仍有可能返回比所请求的字节数要少的数据。)
对TCP来说,这些调用基本上就能满足需求了。但如果使用的是UDP的话,recvfrom和sendto调用也很有用。
#include<sys/socket.h>
int recvfrom(SOCKET s , void *buf, size_t len, int flags,
struct sockaddr *from,int *fromlen);
int sendto(SOCKET s,const void *buf,size_t len ,int flags,
struct sockaddr *to,int tolen);
recvfrom函数调用中的参数from指向一个套接字地址结构,内核在这个地址中存储了输入数据报的源地址。这个地址的长度存储在fromlen指向的一个整数中,注意,fromlen是一个指向整数的指针。
服务器必须在其知名端口上监听客户连接。这使用listen调用来实现这项功能的,但首先必须将接口地址和知名端口号绑定到它的监听套接字上去。这是用bind调用来实现的。
#include<sys/socket.h>
int bind(SOCKET s ,const struct sockaddr *name,int namelen);
返回:成功是返回0,失败时返回-1
参数s是监听套接字的描述符。参数name和namelen提供了要监听的端口和接口。地址通常被设置为INADDR_ANY,说明任何接口都会接受这个连接。如果多宿主主机希望进仅在一个连接上接受连接,它可以指定那个接口的IP地址。
用系统调用listen实现的。它唯一的任务就是把套接字标识为监听状态。当主机收到一个连接请求时,内核会搜索监听套接字列表,查找与连接请求中目的地和端口想匹配的那个套接字。
#include<sys/socket.h>
int listen(SOCKET s, int backlog);
返回:成功是返回0,失败时返回-1
参数s是要标识为监听状态的套接字的描述符。参数backlog是挂起连接的最大数量,它并不是在指定端口上同时可建立连接的最大值,而是排队等待应用程序接受的连接或部分连接的最大数量。
最后一个套接字调用是accept系统调用,负责接受已完成连接队列中的连接。连接一旦被接受,就可以用recv和send这样的调用传输数据了。如果运行成功,accept为可以用于数据传输的新套接字返回一个描述符。
#include<sys/socket.h>
SOCKET accept(SOCKET s, struct sockaddr *addr,int *addrlen);
返回:如果成功就返回一个连接好的套接字,如果失败就返回-1
参数s是监听套接字的描述符。accept会在addr指向的sockaddr_in结构中返回新连接的对等实体地址。内核将这个结构的长度存放在addrlen指向的整数中。通常我们并不关心对等实体的地址,在这种情况下会将addr和addrlen都设置为NULL。