进程间通讯(七).socket(2)

2021-09-15 20:06:44 浏览数 (1)

编译执行

代码语言:javascript复制
emacs@ubuntu:~/c$ alias  gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc tcpclient.x tcpclient.c ; gtc tcpserver.x tcpserver.c
emacs@ubuntu:~/c$ 

此时系统中并没有开放9000端口

代码语言:javascript复制
emacs@ubuntu:~/c$ netstat -ant  | grep 9000
emacs@ubuntu:~/c$ 

运行服务端

代码语言:javascript复制
emacs@ubuntu:~/c$ ./tcpserver.x 
Listening...

此时系统中多了一个9000端口

代码语言:javascript复制
emacs@ubuntu:~/c$ netstat -ant  | grep 9000
tcp        0      0 0.0.0.0:9000            0.0.0.0:*               LISTEN     
emacs@ubuntu:~/c$

运行客户端,会立刻返回

代码语言:javascript复制
emacs@ubuntu:~/c$ ./tcpclient.x 127.0.0.1 hello
2 -->OK
emacs@ubuntu:~/c$ 

服务端会打印信息并且返回

代码语言:javascript复制
emacs@ubuntu:~/c$ ./tcpserver.x 
Listening...
Received a message:hello
emacs@ubuntu:~/c$ 

编译执行过程中没有报错,从结果来看,符合预期


sockaddr 结构体

bits/socket.h 中有关于 sockaddr 结构体的定义

代码语言:javascript复制
/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];           /* Address data.  */
  };

in_addr 结构体

netinet/in.h 中有关于 in_addr 结构体的定义

代码语言:javascript复制
/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

sockaddr_in 结构体

netinet/in.h 中有关于 sockaddr_in 结构体的定义

代码语言:javascript复制
/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
  };

sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)] 中可以看出

这个字段,是为填补与 sockaddr 结构体的长度差

二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别


socket

sys/socket.h 中有关于 socket 函数的声明

代码语言:javascript复制
/* Create a new socket of type TYPE in domain DOMAIN, using
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
   Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;

用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作

__domain 即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址

__type 指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等

__protocol 指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

Note: 并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议


AF_INET 和 SOCK_STREAM 宏定义

bits/socket.h 中有关于 AF_INET 和 SOCK_STREAM 的宏定义

代码语言:javascript复制
emacs@ubuntu:/usr/include$ grep AF_INET bits/socket.h
#define	AF_INET		PF_INET
#define	AF_INET6	PF_INET6
emacs@ubuntu:/usr/include$ grep PF_INET bits/socket.h
#define	PF_INET		2	/* IP protocol family.  */
#define	PF_INET6	10	/* IP version 6.  */
#define	AF_INET		PF_INET
#define	AF_INET6	PF_INET6
emacs@ubuntu:/usr/include$ grep SOCK_STREAM bits/socket.h
  SOCK_STREAM = 1,		/* Sequenced, reliable, connection-based
#define SOCK_STREAM SOCK_STREAM
emacs@ubuntu:/usr/include$

使用此方法可以获取其它想要的宏定义


htons

netinet/in.h 中有关于 htons 的定义

代码语言:javascript复制
/* Functions to convert between host and network byte order.

   Please note that these functions normally take `unsigned long int' or
   `unsigned short int' values as arguments and also return them.  But
   this was a short-sighted decision since on different systems the types
   may have different representations but the values are always the same.  */

extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
     __THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
     __THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
     __THROW __attribute__ ((__const__));

网络字节顺序与系统字节顺序不一定相同

网络字节顺序(大端顺序)是指一个数在内存中存储的时候“高对低,低对高”(即一个数的高位字节存放于低地址单元,低位字节存放在高地址单元中)。但是计算机的内存存储数据时有可能是大端顺序或者小端顺序

而上面的函数就是用来进行这方面转化工作的

  • h:host 本地主机端
  • to:就是to,转化为
  • n:net 网络端
  • l:是 unsigned long (32bit)
  • s:是 unsigned short (16bit)

ntohl 无符号长整型,从网络到本机

ntohs 无符号短整型,从网络到本机

htonl 无符号长整型,从本机到网络

htons 无符号短整型,从本机到网络


INADDR_ANY 宏定义

netinet/in.h 中有关于 INADDR_ANY 的定义

代码语言:javascript复制
/* Address to accept any incoming messages.  */
#define INADDR_ANY              ((in_addr_t) 0x00000000)
/* Address to send to all hosts.  */
#define INADDR_BROADCAST        ((in_addr_t) 0xffffffff)
/* Address indicating an error return.  */
#define INADDR_NONE             ((in_addr_t) 0xffffffff)

INADDR_ANY 代表通配 0.0.0.0


setsockopt

sys/socket.h 中有关于 setsockopt 的定义

代码语言:javascript复制
/* Set socket FD's option OPTNAME at protocol level LEVEL
   to *OPTVAL (which is OPTLEN bytes long).
   Returns 0 on success, -1 for errors.  */
extern int setsockopt (int __fd, int __level, int __optname,
                       __const void *__optval, socklen_t __optlen) __THROW;

__fd 指向一个打开的套接口描述字

__level 指定选项代码的类型

__optname 选项名称

__optval 是一个指向变量的指针,类型为整形

__optlen optval 的size大小

标志打开或关闭某个特征的二进制选项

closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket

代码语言:javascript复制
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

bind

sys/socket.h 中有关于 bind 的定义

代码语言:javascript复制
/* Give the socket FD the local address ADDR (which is LEN bytes long).  */
extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
     __THROW;

0 人点赞