热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linuxsocket编程socket接口

常用socket函数Windows和Linux上常用的socketAPI函数并不多,除了特定操作系统提供的一些基于自身系统特性的API,大多数SocketAPI都源于BSDSock

常用socket函数

Windows 和 Linux 上常用的 socket API 函数并不多,除了特定操作系统提供的一些基于自身系统特性的 API, 大多数 Socket API 都源于BSD Socket (即伯克利套接字(Berkeley Sockets)),因此这些 socket 函数在不同的平台有着相似的签名和参数。

经常有想学习网络编程的新人询问要掌握哪些基础的socket API,我这里给一个简单的函数列表,列表中给出的都是应该熟练掌握的 socket 函数。

​ 常用 Berkeley Sockets API 一览表

函数名称函数简单描述附加说明
socket创造某种类型的套接字
bind将一个 socket绑定一个ip与端口的二元组上
listen将一个 socket 变为侦听状态
connect试图建立一个 TCP 连接一般用于客户端
accept尝试接收一个连接一般用于服务端
send通过一个socket发送数据
recv通过一个socket收取数据
select判断一组socket上的读事件
gethostbyname通过域名获取机器地址
close关闭一个套接字,回收该 socket 对应的资源Windows 系统中对应的是 closesocket
shutdown关闭 socket 收或发通道
setsockopt设置一个套接字选项
getsockopt获取一个套接字选项

socket

socket()函数的原型如下,这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。

#include /* See NOTES */
#include
int socket(int domain, int type, int protocol);

  • domain

函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h中定义。

名称含义名称含义
PF_UNIX PF_LOCAL本地通信PF_X25ITU-T X25 / ISO-8208协议
AF_INET,PF_INETIPv4 Internet协议PF_AX25Amateur radio AX.25
PF_INET6IPv6 Internet协议PF_ATMPVC原始ATM PVC访问
PF_IPXIPX-Novell协议PF_APPLETALKAppletalk
PF_NETLINK内核用户界面设备PF_PACKET底层包访问
  • type

type 函数socket()的参数type用于设置套接字通信的类型,主要有SOCKET_STREAM(流式套接字)、SOCK——DGRAM(数据包套接字)等。

名称含义
SOCK_STREAMTcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输
SOCK_DGRAM支持UDP连接(无连接状态的消息)
SOCK_SEQPACKET序列化包,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,数据长度定常。每次调用读系统调用时数据需要将全部数据读出
SOCK_RAWRAW类型,提供原始网络协议访问
SOCK_RDM提供可靠的数据报文,不过可能数据会有乱序

并不是所有的协议族都实现了这些协议类型,例如,AF_INET协议族就没有实现SOCK_SEQPACKET协议类型。

  • protocol

函数socket()的第3个参数protocol用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。

  • errno
    函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获得:
含义
EACCES没有权限建立制定的domain的type的socket
EAFNOSUPPORT不支持所给的地址类型
EINVAL不支持此协议或者协议不可用
EMFILE进程文件表溢出
ENFILE已经达到系统允许打开的文件数量,打开文件过多
ENOBUFS/ENOMEM内存不足。socket只有到资源足够或者有进程释放内存
EPROTONOSUPPORT制定的协议type在domain中不存在

比如我们建立一个流式套接字可以这样:

int sock = socket(AF_INET, SOCK_STREAM, 0);

bind

在套接口中,一个套接字只是用户程序与内核交互信息的枢纽,它自身没有太多的信息,也没有网络协议地址和 端口号等信息,在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程。许多时候内核会我们自动绑定一个地址,然而有时用 户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址或端口以等待客户来连接。这个事由 bind的函数完成。

int bind( int sockfd, struct sockaddr* addr, socklen_t addrlen)

  • sockfd 就是我们调用socket函数后创建的socket 句柄或者称文件描述符号。
  • addr addr是指向一个结构为sockaddr参数的指针,sockaddr中包含了地址、端口和IP地址的信息。在进行地址绑定的时候,需要弦将地址结构中的IP地址、端口、类型等结构struct sockaddr中的域进行设置之后才能进行绑定,这样进行绑定后才能将套接字文件描述符与地址等接合在一起。

由于历史原因,我们前后有两个地址结构: struct sockaddr 该结构定义如下:

struct sockaddr {
uint8_t sa_len;  
unsigned short sa_family; /* 地址家族, AF_xxx */   
char sa_data[14]; /*14字节协议地址*/   
};

其实这个结构逐渐被舍弃,但是也还是因为历史原因,在很多的函数,比如connect、bind等还是用这个作为声明,实际上现在用的是第二个结构,我们需要把第二个结构强转成sockaddr。 struct sockaddr_in 其定义如下:

struct sockaddr_in {
   uint8_t sa_len; /* 结构体长度*/
short int sin_family; /* 通信类型 */
   unsigned short int sin_port; /* 端口 */
   struct in_addr sin_addr; /* Internet 地址 */
   unsigned char sin_zero[8]; /* 未使用的*/
   };
struct in_addr { //sin_addr的结构体类型in_addr 原型
   unsigned long s_addr; /*存4字节的 IP 地址(使用网络字节顺序)。*/
   };

在使用的时候我们必须指定通信类型,也必须把端口号和地址转换成网络序的字节序

  • addrlen addr结构的长度,可以设置成sizeof(struct sockaddr)。使用sizeof(struct sockaddr)来设置套接字的类型和其对已ing的结构。

bind()函数的返回值为0时表示绑定成功,-1表示绑定失败,errno的错误值如表1所示。

含义备注
EADDRINUSE给定地址已经使用
EBADFsockfd不合法
EINVALsockfd已经绑定到其他地址
ENOTSOCKsockfd是一个文件描述符,不是socket描述符
EACCES地址被保护,用户的权限不足
EADDRNOTAVAIL接口不存在或者绑定地址不是本地UNIX协议族,AF_UNIX
EFAULTmy_addr指针超出用户空间UNIX协议族,AF_UNIX
EINVAL地址长度错误,或者socket不是AF_UNIX族UNIX协议族,AF_UNIX
ELOOP解析my_addr时符号链接过多UNIX协议族,AF_UNIX
ENAMETOOLONGmy_addr过长UNIX协议族,AF_UNIX
ENOENT文件不存在UNIX协议族,AF_UNIX
ENOMEN内存内核不足UNIX协议族,AF_UNIX
ENOTDIR不是目录UNIX协议族,AF_UNIX

比如这样:

struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY; if (bind(sfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) <0)
{
perror("bind");
exit(1);
}

listen

int listen(int sockfd, int backlog);

listen()函数将sockfd标记为被动打开的套接字,并作为accept的参数用来接收到达的连接请求。

  • sockfd是一个套接字类型的文件描述符,具体类型为SOCK_STREAM或者SOCK_SEQPACKET。
  • backlog参数用来描述sockfd的等待连接队列能够达到的最大值。当一个请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,或者如果底层协议支持重传(比如tcp协议),本次请求会被丢弃不作处理,在下次重试时期望能连接成功(下次重传的时候队列可能已经腾出空间)。 

说起这个backlog就有一点儿历史了,等下文描述。

  • errno
含义
EADDRINUSE另一个套接字已经绑定在相同的端口上。
EBADF参数sockfd不是有效的文件描述符。
ENOTSOCK参数sockfd不是套接字。
EOPNOTSUPP参数sockfd不是支持listen操作的套接字类型。

connect

声明如下

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明如下

  • sockfd 是系统调用 socket() 返回的套接字文件描述符。
  • serv_addr 是 保存着目的地端口和 IP 地址的数据结构 struct sockaddr_in。
  • addrlen 设置 为 sizeof(struct sockaddr_in)

errno
connect函数在调用失败的时候返回值-1,并会设置全局错误变量 errno。

含义
EBADF参数sockfd 非合法socket处理代码
EFAULT参数serv_addr指针指向无法存取的内存空间
ENOTSOCK参数sockfd为一文件描述词,非socket。
EISCONN参数sockfd的socket已是连线状态
ECONNREFUSED连线要求被server端拒绝。
ETIMEDOUT企图连线的操作超过限定时间仍未有响应。
ENETUNREACH无法传送数据包至指定的主机。
EAFNOSUPPORTsockaddr结构的sa_family不正确。

accept

函数声明

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数说明
sockfd是由socket函数返回的套接字描述符,参数addr和addrlen用来返回已连接的对端进程(客户端)的协议地址。如果我们对客户端的协议地址不感兴趣,可以把arrd和addrlen均置为空指针。

返回值

成功时,返回非负整数,该整数是接收到套接字的描述符;出错时,返回-1,相应地设定全局变量errno。

含义
EBADF非法的socket
EFAULT参数addr指针指向无法存取的内存空间
ENOTSOCK参数s为一文件描述词,非socket
EOPNOTSUPP指定的socket并非SOCK_STREAM
EPERM防火墙拒绝此连线
ENOBUFS系统的缓冲内存不足
ENOMEM核心内存不足

特别需要说明下的是,这个accept是一个阻塞式的函数,对于一个阻塞的套套接字,一直阻塞,或者返回一个错误值,对于非阻塞套接字。accept有可能返回-1,但是如果errno的值为,EAGAIN或者EWOULDBLOCK,此时需要重新调用一次accept函数。

send_recv send和recv函数

函数申明如下

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  • sockfd :套接字
  • buf : 待发送或者接收的缓存
  • len : 如果是recv指期望接收的长度,如果是send指要发送的长度。
  • flags : 标志位,取值如下表:
flags说明recvsend
MSG_DONTROUTE绕过路由表查找
MSG_DONTWAIT仅本操作非阻塞
MSG_OOB     发送或接收带外数据
MSG_PEEK  窥看外来消息
MSG_WAITALL   等待所有数据

errno

含义 
EAGAIN套接字已标记为非阻塞,而接收操作被阻塞或者接收超时 
EBADFsock不是有效的描述词 
ECONNREFUSE远程主机阻绝网络连接 
EFAULT内存空间访问出错 
EINTR操作被信号中断 
EINVAL参数无效 
ENOMEM内存不足 
ENOTCONN与面向连接关联的套接字尚未被连接上 
ENOTSOCKsock索引的不是套接字 当返回值是0时,为正常关闭连接;

当返回值为-1时是不是一定就错误了,当返回值为0时该怎么做呢?

  • 如何正确判断一个对端已经关闭了连接?

/*客户端设置非阻塞,然后判断链接是否成功*/
int SocketConnectWithTimeout
(
int mySocket,
struct mySocketaddr *adrs,
int adrsLen,
struct timeval *timeVal
)
{
int flag;
fd_set writeFds;
int remotPeerAdressLen;
struct mySocketaddr remotPeerAdress; if(timeVal == NULL)
{
return (connect(mymySocket, adrs, adrsLen));
}

flag = fcntl(mySocket, F_GETFL, 0);
fcntl(mySocket, F_SETFL, flag | O_NONBLOCK);//修改当前的flag标志为给阻塞 //对于非阻塞式套接字,如果调用connect函数会之间返回-1(表示出错),且错误为EINPROGRESS,表示连接建立
if(connect(mySocket, adrs, adrsLen) <0)
{
//当使用非阻塞模式的时候,如果链接没有被立马建立,则connect()返回EINPROGRESS
if(errno == EINPROGRESS)
{
//select是一种IO多路复用机制,它允许进程指示内核等待多个事件的任何一个发生,并且在有一个或者多个事件发生或者经历一段指定的时间后才唤醒它。
//connect本身并不具有设置超时功能,如果想对套接字的IO操作设置超时,可使用select函数。此时我们使用不断的检测writeFds来判断链接的建立?
FD_ZERO(&writeFds);
FD_SET((unsigned int)mySocket, &writeFds); if(select(FD_SETSIZE, (fd_set *)NULL, &writeFds, (fd_set *)NULL, timeVal) > 0)
{
//select()成功了,查看mySocketet是否可写(关键)
if (FD_ISSET ((unsigned int)mySocket, &writeFds))
{
//已经可写了,此时我们要通过使用getpeername()判断是否真正的链接成功,如果返回值不是-1;
//说明connect()成功了。
remotPeerAdressLen = sizeof (remotPeerAdress);
if(getpeername (mySocket, &remotPeerAdress, &remotPeerAdressLen) != ERROR)
{
return OK;
}
else
{
return ERROR;
}
}
}
}
else
{
return ERROR;
}
}
fcntl(mySocket, F_SETFL, flag);//恢复标志位为阻塞
}

  • 1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完成(有的系统用FNEDLAY也可).

    2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧行还没有完成. 

    3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,如果可写用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int)); 来得到error的值,如果为零,则connect成功.


推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了使用readlink命令获取文件的完整路径的简单方法,并提供了一个示例命令来打印文件的完整路径。共有28种解决方案可供选择。 ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
author-avatar
最棒的aaaaaaaaa2_531
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有