linux TCP网络编程(服务端函数简介)

本文详细介绍了socket函数的功能,包括其用于创建通信端点的过程,以及bind和listen操作的使用方法。同时涵盖了各种通信协议族和套接字类型,以及可能出现的错误代码及其含义。
摘要由CSDN通过智能技术生成

函数说明

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));
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值