函数说明
socket
/****************************************************************************
函 数 名 : socket
功能描述 : 为通信创建一个端点,并返回一个指向该端点的文件描述符,该文件描述符是当前进程未打开的最小描述符。
输入参数 : int domain 通信的协议族 (AF_INET TCP IPV4)
int type 套接字类型 (SOCK_STREAM)
int protocol 指定套接字使用的特定协议 TCP用0就好(只有一个协议存在)
返 回 值 : int 0 成功 -1 失败 并设置错误码
**************************************************************************/
int socket(int domain, int type, int protocol);
通信协议族选项:
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7) TCP IPV4
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK AppleTalk ddp(7)
AF_PACKET Low level packet interface packet(7)
AF_ALG Interface to kernel crypto API
套接字类型:
SOCK_STREAM 提供有序的、可靠的、双向的、基于连接的字节流。可以支持带外数据传输机制. TCP
SOCK_DGRAM 支持数据报(无连接、最大长度固定的不可靠消息) UDP
SOCK_SEQPACKET 为固定最大长度的数据报提供一个有序、可靠、基于双向连接的数据传输路径;每次输入系统调用都要求使用者读取整个数据包
SOCK_RAW 提供原始网络协议访问。
SOCK_RDM 提供不保证排序的可靠数据报层。
SOCK_PACKET Obsolete and should not be used in new programs; see packet(7).
错误码:
EACCES 创建指定类型和/或协议套接字的权限被拒绝
EAFNOSUPPORT 实现不支持指定的地址族。
EINVAL 未知协议,或协议族不可用或套接字类型无效
EMFILE 已达到每个进程打开的文件描述符数量限制。
ENOBUFS or ENOMEM 可用内存不足。在释放足够的资源之前,无法创建套接字。
EPROTONOSUPPORT 该域不支持协议类型或指定的协议。其他错误可能由底层协议模块产生
bind
/****************************************************************************
结构体:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
IP套接字地址定义为IP接口地址和16位端口号的组合。基本IP协议不提供端口号,它们由更高级别的协议实现,如udp(7)和tcp(7)。在原始套接字上,sin端口设置为IP协议。
结构体查找 终端输入man 7 ip
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; //网络字节顺序的接口地址
};
函 数 名 : bind
功能描述 : 当使用socket(2)创建套接字时,它存在于名称空间(地址族)中,但没有为其分配地址。Bind()将addr指定的地址分配给文件描述符sockfd引用的套接字。Addrlen以字节为单位指定addr所指向的地址结构的大小。传统上,这个操作称为“为套接字分配名称”
输入参数 : int sockfd socket得到的套接字
const struct sockaddr *addr 待绑定的地址
socklen_t addrlen 待绑定地址信息的长度
返 回 值 : int 0 成功 -1 失败 并设置错误码
**************************************************************************/
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
错误码:
EACCES 该地址受到保护,并且该用户不是超级用户.
EADDRINUSE 地址被占用(端口重复使用时)
EADDRINUSE
(Internet domain sockets) The port number was specified as zero in the socket address structure, but, upon attempting to bind to an ephemeral port, it was
determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
EBADF 套接字无效.
EINVAL 套接字已经被绑定.
EINVAL 待绑定地址信息的长度无效(bind(sock, (struct sockaddr *)&address, 1);), ip无效 (address.sin_addr.s_addr = 10;).
ENOTSOCK
The file descriptor sockfd does not refer to a socket.
The following errors are specific to UNIX domain (AF_UNIX) sockets:
EACCES Search permission is denied on a component of the path prefix. (See also path_resolution(7).)
EADDRNOTAVAIL
A nonexistent interface was requested or the requested address was not local.
EFAULT addr points outside the user's accessible address space.
ELOOP Too many symbolic links were encountered in resolving addr.
ENAMETOOLONG
addr is too long.
ENOENT A component in the directory prefix of the socket pathname does not exist.
ENOMEM 内存不足.
ENOTDIR
A component of the path prefix is not a directory.
EROFS 只读系统上创建的套接字.
listen
/****************************************************************************
函 数 名 : listen
功能描述 : 为通信创建一个端点,并返回一个指向该端点的文件描述符,该文件描述符是当前进程未打开的最小描述符。
输入参数 : int sockfd socket得到的套接字
int backlog 监听的最大数量,数量满时收到会给个ECONNREFUSED,如果底层支持重传可能会收到
返 回 值 : int 0 成功 -1 失败 并设置错误码
**************************************************************************/
int listen(int sockfd, int backlog);
注意点:监听的最大数量不是连接的客户端数量
TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。
此时根据监听数量将无法连接的客户端加入内核队列 上限时监听数量
错误码:
EADDRINUSE
1.与另一个套接字端口重复 2.调用前没有bind
EBADF 套接字无效.
ENOTSOCK
文件描述符不是套接字
EOPNOTSUPP
套接字的类型不支持listen()操作。
##accept
函 数 名 : accept
功能描述 : 从监听列表中取出第一个建立tcp连接
输入参数 : int sockfd socket得到的套接字
struct sockaddr *addr 连接地址信息
socklen_t *addrlen 连接地址信息的长度
返 回 值 : int 0 成功 -1 失败 并设置错误码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
错误:
EAGAIN or EWOULDBLOCK Resource temporarily unavailable 套接字为非阻塞属性,accept无新连接
EBADF 套接字无效.
ECONNABORTED
A connection has been aborted.
EFAULT The addr argument is not in a writable part of the user address space.
EINTR 系统调用被中断
EINVAL 套接字未监听或者addrlen无效(不等于sizeof(socklen_t) )
EINVAL (accept4()) invalid value in flags.
EMFILE The per-process limit on the number of open file descriptors has been reached.
ENFILE The system-wide limit on the total number of open files has been reached.
ENOBUFS, ENOMEM
Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.
ENOTSOCK
文件描述符不是套接字.
EOPNOTSUPP
The referenced socket is not of type SOCK_STREAM.
EPROTO Protocol error.
In addition, Linux accept() may fail if:
EPERM 防火墙规则禁止连接.
实例代码
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#define SERVER_PORT 9999
#define LISTEN_MAX 5
#if 0
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; //网络字节顺序的接口地址
};
#endif
int main(void *arg, char *argv[])
{
int sock = -1;
int listen_sock = -1;
int ret = 0;
int max_fd = -1;
unsigned long optval = 1;
struct sockaddr_in address;
struct sockaddr_in remote_addr;
socklen_t remote_addrlen = sizeof(remote_addr);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sock)
{
printf("socket fail\r\n");
return -1;
}
//一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
//设置为非阻塞模式
//ioctl(sock, FIONBIO, &optval);
address.sin_family = AF_INET;
address.sin_port = htons(SERVER_PORT); //转网络字节序
address.sin_addr.s_addr = INADDR_ANY; //本地任意地址 address.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
if (-1 == ret)
{
printf("bind fail error:%s\r\n", strerror(errno));
return -1;
}
ret = listen(sock, LISTEN_MAX);
while (1)
{
listen_sock = accept(sock, (struct sockaddr *)&remote_addr, (socklen_t *)&remote_addrlen);
if (-1 == listen_sock)
{
printf("listen fail error:%s\r\n", strerror(errno));
continue;
}
printf("remote addr:%s, port:%d\r\n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
}
}