socket 套接字是一种进程间的通信的方法,不同于以往介绍的进程间通信方法的是,它并不局限于同一台计算机的资源,例如文件系统空间,共享内存或者消息队列。套接字可以认为是对管道概念的扩展——一台机器上的进程可以使用套接字与另一台机器上的进程通信。因此客户与服务器可以分散在网络中。同一台机器上的进程间也可以用套接字通信。套接字是一种通信机制,客户/服务器系统既可以在本地单机上运行,也可以在网络中运行。套接字与管道的区别:它明确区分客户与服务器,可以实现将多个客户连接到一个服务器。
可以先看linux下socket编程常用的函数:
linux 下的socket API:
socket
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol);
#include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ tcp_socket = socket(AF_INET, SOCK_STREAM, 0); udp_socket = socket(AF_INET, SOCK_DGRAM, 0); raw_socket = socket(AF_INET, SOCK_RAW, protocol);
sockaddr 与 sockaddr_in
sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下: struct sockaddr { sa_family_t sin_family;//地址族 char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息 };
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下: struct sockaddr_in { sa_family_t sin_family; //地址族 uint16_t sin_port; //16位TCP/UDP端口号 struct in_addr sin_addr; //32位IP地址 char sin_zero[8]; //不使用 }; 该结构体中的另一个结构体in_addr定义如下,它用来存放32位IP地址。 struct in_addr { In_addr_t s_addr; //32位IPv4地址 };
网络字节序与主机字节序
htonl()--"Host to Network Long" ntohl()--"Network to Host Long" htons()--"Host to Network Short" ntohs()--"Network to Host Short"
bind 服务端套接字绑定自己的IP地址与端口号
#include <sys/socket.h> int bind(int socket, const struct sockaddr *address, socklen_t address_len);
connect函数
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); TCP客户端通过connect函数与服务端连接,进行通信。
listen函数
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int listen(int sockfd, int backlog); (1) 执行listen 之后套接字进入被动模式。 (2) 队列满了以后,将拒绝新的连接请求。客户端将出现连接D 错误WSAECONNREFUSED。 (3) 在正在listen的套接字上执行listen不起作用。
accept函数
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); accept()系统调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET。它提取出所监听套接字的等待连接队列中第一个连接请求, 创建一个新的套接字,并返回指向该套接字的文件描述符。新建立的套接字不在监听状态,原来所监听的套接字也不受该系统调用的影响。 备注:新建立的套接字准备发送send()和接收数据recv()。 参数: sockfd,利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址(一般为服务器的套接字),并且通过listen()一直在监听连接; addr,指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址(一般为客户端地址)填写,返回地址addr的确切格式由套接字的地址类别 (比如TCP或UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,应该置为NULL; 备注:addr是个指向局部数据结构sockaddr_in的指针,这就是要求接入的信息本地的套接字(地址和指针)。 addrlen,一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值; 备注:addrlen是个局部整形变量,设置为sizeof(struct sockaddr_in)。 如果队列中没有等待的连接,套接字也没有被标记为Non-blocking,accept()会阻塞调用函数直到连接出现;如果套接字被标记为Non-blocking,队列 中也没有等待的连接,accept()返回错误EAGAIN或EWOULDBLOCK。 备注:一般来说,实现时accept()为阻塞函数,当监听socket调用accept()时,它先到自己的receive_buf中查看是否有连接数据包; 若有,把数据拷贝出来,删掉接收到的数据包,创建新的socket与客户发来的地址建立连接;若没有,就阻塞等待;
send、recv、write、read读写数据
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);
close 关闭套接字
#include <unistd.h> int close(int fd);欢迎大家加我的群: 460952208