套接字通信

背景

基于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,有更多的控制

示例代码

参考

猜你喜欢

转载自www.cnblogs.com/orejia/p/12128866.html