自觉此心无一事
小鱼跳出绿萍中
概念
Socket
Socket原指IP地址和端口对(ip,port),是TCP通信的一端,其唯一标志一个通信端。在Linux系统中,与socket相关的api大多封装在sys/socket.h头文件中,包括创建socket、命名socket、监听socket、接受连接、发起连接、读写数据 等等。
字节序
现代CPU的累加器一次至少装载4字节,这4字节在内存中排列的顺序就是字节序。字节序分为大端字节序 和 小端字节序。大端序指的是一个整数的高位字节(23~31bit)存储在内存低地址处,低位字节(0~7bit)存储在内存的高地址处;小端序正好相反。
PS:现代PC大多采用小端序,因此小端序又叫主机字节序;大端序又叫网络字节序,默认发送端采用大端序
协议族和地址族
-
协议族和地址族的关系
协议族 地址族 描述 PF_UNIX AF_UNIX UNIX本地协议族 PF_INET AF_INET TCP/IPv4协议族 PF_INET6 AF_INET6 TCP/IPv6协议族 -
协议族及其地址值
协议族 地址值含义和长度 PF_UNIX 文件路径名,可达108字节 PF_INET 16bit端口号和32bit IPv4地址;6字节 PF_INET6 16bit端口号,32bit流标志,128bit IPv6地址,32bit范围ID;26字节
C/S通信
-
创建socket
#include <sys/types.h> #include <sys/socket.h> int socket(int domain,int type,int protocol)
- domain指定协议族(PF_UNIX、PF_INET、PF_INET6)
- type指定服务类型(SOCK_STREAM、SOCK_UGRAM)
- protocol选择一个具体协议,默认为0
创建成功则返回一个socket文件描述符,失败则返回-1
-
绑定socket
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
创建socket时,我们指定了地址族,但并未指定具体哪个socket地址,因此需要将socket与socket地址绑定
- sockfd是socket文件描述符
- addr是地址
- addrlen说明地址长度
绑定成功返回0,失败返回-1,常见error是:
- EACCESS(绑定受保护地址,0~1023)
- EADDRINUSE(绑定被占用地址)
-
监听socket
#include <sys/socket.h> int listen(int sockfd,int backlog)
backlog是内核监听队列的最大长度,监听队列长度如果超过最大长度,服务器将不再监听,典型值是5
-
接受连接
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd,struct sockaddr* addr, socklen_t addrlen)
成功时返回一个新的连接socket,服务器可通过此socket与客户端通信,失败返回-1
-
发起连接
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd,struct sockaddr* addr, socklen_t addrlen)
-
关闭连接
#include <unistd.h> int close(int fd)
fd是待关闭的socket描述符,不过close系统调用并非立即关闭一个连接,而是给该fd的引用计数器-1,直到为0,才关闭,若要立即终止,可以使用shutdown系统调用
#include <sys/socket.h> int shutdown(int sockfd,int howto)
howto参数:
值 含义 SHUT_RD 关闭读的这一半,接收缓冲区数据丢失 SHUT_WR 关闭写的这一半,发送缓冲区会被全部发送出去 SHUT_RDWR 全部关闭 -
TCP数据读写
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void* buf, int flags) ssize_t send(int sockfd, const void* buf, size_t len, int flags)
-
UDP数据读写
#include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void* buf, int flags,struct sockaddr* src_addr, socklen_t* addrlen) ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const sockaddr* dest_addr, socklen_t addrlen)
-
通用数据读写
#include <sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags) ssize_t sendmsg(int sockfd, struct msghdr* msg, size_t len, int flags)
基本操作
server
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc, char const *argv[])
{
int sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
cout << "create socket failed..." << endl;
return -1;
}
cout << "create socket successed..." << endl;
struct sockaddr_in addrin;
addrin.sin_family = PF_INET;
addrin.sin_port = 8080;
addrin.sin_addr.s_addr = inet_addr("127.0.0.1");
int bind_status = bind(sock, (struct sockaddr*)&addrin, sizeof(addrin));
if (bind_status == -1)
{
cout << "bind socket failed..." << endl;
return -1;
}
cout << "bind socket successed..." << endl;
int listen_status = listen(sock,5);
if (listen_status == -1)
{
cout << "listen socket failed..." << endl;
return -1;
}
cout << "listen socket successed..." << endl;
socklen_t len = sizeof(addrin);
int sockfd = accept(sock, (struct sockaddr*)&addrin, &len);
if (sockfd == -1)
{
cout << "accept socket failed..." << endl;
return -1;
}
cout << "accept socket successed..." << endl;
string buf(512,'\0');
int recv_status = recv(sockfd, const_cast<char*>(buf.c_str()), 512, 0);
if (recv_status == -1)
{
cout << "receive data from socket failed..." << endl;
return -1;
}
cout << "receive data from socket successed..." << endl;
cout << buf << endl;
close(sockfd);
close(sock);
return 0;
}
client
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc, char const *argv[])
{
int sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
cout << "create socket failed..." << endl;
return -1;
}
cout << "create socket successed..." << endl;
struct sockaddr_in addrin;
addrin.sin_family = PF_INET;
addrin.sin_port = 8080;
addrin.sin_addr.s_addr = inet_addr("127.0.0.1");
int connect_status = connect(sock, (struct sockaddr*)&addrin, sizeof(addrin));
if (connect_status == -1)
{
cout << "connect socket failed..." << endl;
return -1;
}
cout << "connect socket successed..." << endl;
string data = "Hello World";
int send_status = send(sock,data.c_str(),data.length(),0);
if (send_status == -1)
{
cout << "send data to socket failed..." << endl;
return -1;
}
cout << "send data to socket successed..." << endl;
close(sock);
return 0;
}