背景
基于C语言,对linux系统下套接字通信相关的知识点进行梳理,比如重点概念的理解,重点操作函数的解析等,最后附上相关示例代码。
概念
套接字分类
- 流式套接字(SOCK_STREAM)
- 数据报套接字(SOCK_DGRAM)
- 原始套接字
流式套接字
使用TCP(传输控制协议)进行数据传输,可以保证数据传输的准确性。
数据报套接字
使用UDP(使用者数据报协议)进行数据传输,不能保证接收的数据的准确性。
相关数据结构
struct sockaddr
#include <sys/socket.h>struct sockaddr { unsigned short sa_family;//地址协议族 char sa_data[14];//地址(ip + port) };
struct sockaddr 是通用的套接字地址,长度为16字节。
struct sockaddr_in
#include <netinet/in.h>
/* Internet address. */
struct in_addr
{
uint32_t s_addr;
};
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
unsigned short sa_family;
uint16_t sin_port; /* Port number. */必须是网络字节序
struct in_addr sin_addr; /* Internet address. */必须是网络字节序
unsigned char sin_zero[8];/* Pad to size of `struct sockaddr‘. */
};
internet环境下套接字的地址形式,长度也是16字节;
因为bind()函数的套接字地址类型是通用类型,所以现在通行的做法是,使用struct sockaddr_in绑定ip和端口,然后强转成struct sockaddr类型
本机转换
由于struct sockaddr_in的Ip和端口是数据需要发送到网络端,所以类型必须是网络字节序;
端口的转换需要用到下面的htons
#include <arpa/inet.h>uint16_t htons(uint16_t hostshort); uint32_t htonl(uint32_t hostlong); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
其实,ip的转换也可以用htonl,但入参是uint32_t,需要先将一个字符串类型的IP换算成数值类型再传参;
考虑到htonl的使用有些繁琐,一般我们使用下面的函数来进行地址的转换:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> in_addr_t inet_addr(const char *cp); int inet_aton(const char *cp, struct in_addr *inp); char *inet_ntoa(struct in_addr in);
inet_addr()和inet_aton()都可以用于获取一个网络字节序的地址;
inet_ntoa是逆操作;
#define INADDR_ANY ((in_addr_t) 0x00000000)
INADDR_ANY是一个宏定义,数值是网络字节序,等价于inet_addr("0.0.0.0"),功能是代码所有本机IP
socket()
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain 网络通信协议族,一般写AF_INET
type 通信类型,SOCK_STREAM|SOCK_DGRAM
protocol 定义额外的一个通信协议。通常只需要一个协议,所以这里填0
返回:成功返回一个可用套接字;失败返回-1,并重置errno
bind()
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
给套接字绑定到 “本机通信地址”返回:
成功返回一个可用套接字;失败返回-1,并重置errno
connect()
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
将套接字与远程服务器通信地址绑定
返回:成功返回一个可用套接字;失败返回-1,并重置errno
listen()
int listen(int sockfd, int backlog);
sockfd一般是服务器的网络侦听套接字,backlog是连接队列的长度(等待接受连接请求)
返回:成功返0;失败返回-1并重置errno
accept()
#include <sys/types.h> #include <sys/socket.h> int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
返回一个成功建立连接的新套接字
返回:成功返0;失败返回-1并设置errno
send()
#include <sys/types.h> #include <sys/socket.h> int send(int s, const void *msg, size_t len, int flags); int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); int sendmsg(int s, const struct msghdr *msg, int flags);
s 套接字
msg 待发送的数据
len 数据长度
flags 填0
close()
#include <unistd.h> int close(int fd);
完全关闭连接
#include <sys/socket.h> int shutdown(int sockfd, int how); how: --SHUT_RD 关闭读端 --SHUT_WR 关闭写端 --SHUT_RDWR 关闭读写(同close())
相比close,有更多的控制
示例代码
参考
原文地址:https://www.cnblogs.com/orejia/p/12128866.html