1、socket
1、什么是socket
socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket
这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
个人理解:将底层复杂的协议体系、执行流程进行封装得到的接口,以便于调用协议进行通信。
2、本质
unsigned int 就是一个整数,一种数据类型,标识当前的应用程序,协议等信息。
3、应用:
通过socket作为客户端服务器的唯一身份标识
4、socket函数参数
填0的时候,由系统决定填什么
socket(
_In_ int af, //地址的类型 AF_INET AF_INET6 AF_BTH
_In_ int type, //套接字类型 SOCK_STREAM...
_In_ int protocol //协议的类型 IPPROTO。。。
);
常用套接字类型
<1>流式套接字(SOCK_STREAM)—TCP
提供面向连接的、可靠的传输服务,数据无差错,无重复的发送,
且按发送顺序接收。
<2>数据报式套接字(SOCK_DGRAM)(UDP)
提供无连接服务。不提供无差错保证,数据可能丢失或者重复,并且接收顺序混乱。
<3>原始套接字(SOCK_RAW)(端口扫描工具。。。自己写的包)
SOCKET socketserver = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (INVALID_SOCKET == socketserver)
{
int a = WSAGetLastError();//获取错误码
printf("socket创建失败");
WSACleanup();
}
closesocket(socketserver);
2、bind函数
1、作用
为socket绑定端口号与具体地址
2、 参数
WSAAPI
bind(
_In_ SOCKET s, //上个函数创建的socket,绑定了协议信息,bind绑定实际的地址以及端口号
_In_reads_bytes_(namelen) const struct sockaddr FAR * name,//对应的sockaddr_in结构体
_In_ int namelen 参数2类型的大小
);
sockaddr_in结构体内容
/*
* Socket address, internet style.
*/
struct sockaddr_in {
short sin_family; //地址族
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sin_zero[8];//不使用
};
IP地址结构体原型:
//
// IPv4 Internet address
// This is an 'on-wire' format structure.
//
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
返回值
值 | 含义 |
---|---|
EADDRINUSE | 给定地址已经使用 |
EBADF | sockfd不合法 |
EINVAL | sockfd已经绑定到其他地址 |
ENOTSOCK | sockfd是一个文件描述符,不是socket描述符 |
EACCES | 地址被保护,用户的权限不足 |
EADDRNOTAVAIL | 接口不存在或者绑定地址不是本地 |
EFAULT | my_addr指针超出用户空间 |
EINVAL | 地址长度错误,或者socket不是AF_UNIX族 |
ELOOP | 解析my_addr时符号链接过多 |
ENAMETOOLONG | my_addr过长 |
ENOENT | 文件不存在 |
ENOMEN | 内存内核不足 |
ENOTDIR | 不是目录 |
EROFS | socket节点应该在制度文件系统上 |
demo
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons(1234);
//service.sin_addr.S_un.S_un_b.s_b1 = 127;
//service.sin_addr.S_un.S_un_b.s_b2 = 0;
//service.sin_addr.S_un.S_un_b.s_b3 = 0;
//service.sin_addr.S_un.S_un_b.s_b4 = 1;
service.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int bres = bind(socketserver,(const SOCKADDR*)&service,sizeof(service));
if (SOCKET_ERROR == bres)
{
int a = WSAGetLastError();
closesocket(socketserver);
WSACleanup();
}
3、listen函数
作用
将套接字置于正在侦听传入连接的状态
参数
int
WSAAPI //调用约定
listen(
_In_ SOCKET s, //服务器端的socket
_In_ int backlog //挂起连接队列的最大长度
);
SOMAXCONN 由系统设置最大长度 调用约定作用:
1、函数名字的编译方式 2、参数的入栈顺序 3、函数的调用时间
返回值: 成功 0 失败 SOCKET_ERROR 可以调用WSAGetLastError() 然后释放
demo:
int lres = listen(socketserver,SOMAXCONN);
if (SOCKET_ERROR == lres)
{
int a = WSAGetLastError();
closesocket(socketserver);
WSACleanup();
return 0;
}
4、accept
作用:
允许在套接字上进行连接尝试(将客户端信息创建成客户端socket,提供给服务器通信)
参数:
SOCKET
WSAAPI
accept(
_In_ SOCKET s,//服务器端的socket
_Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR * addr, //客户端地址端口信息结构体
_Inout_opt_ int FAR * addrlen //参数2的大小
);
返回值
0 成功 失败 INVALID_SOCKET 可以调用WSAGetLastError() 获取失败原因然后释放
此时可以通过getpeername()获取客户端信息
getsockname()获取本地服务器信息
demo
5、recv
作用:
接受客户端发送来的消息(阻塞)
原理:
通过socket找到接受协议数据的缓冲区,并将数据复制到参数二中
函数参数
int
WSAAPI
recv(
_In_ SOCKET s,//客户端对应的socket
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, //客户端消息存储空间,一般1500字节
_In_ int len,//一般是参数二得到的字节数-1,预留‘\0’
_In_ int flags//数据的读取方式 一般填0
);
读取数据的方式(flag):
MSG_PEEK:读取数据后不进行删除(每次读取的内容相同) MSG_OOB:接受带外数据:传递的数据在最后字写携带特殊数据
MSG_WAITALL:等到缓冲区字节满足参数3请求的字节数后,才开始读取
返回值:
读出来的字节大小 客户端下限,返回0,释放客户端socket 执行失败
返回SOCKET_ERROR,WSAGetLastError获取错误码
demo:
char buf[1500] = { 0 };
int rres = recv(socketClient,buf,10,0);
if (0 == rres)
{
printf("连接中断");
}
else if (SOCKET_ERROR == rres)
{
int a = WSAGetLastError();//获取错误码
printf("socket创建失败");
closesocket(socketserver);
WSACleanup();
return 0;
}
else
{
printf("%d, %s \n", rres, buf);
}
6、send
作用:
向目标发送数据,将数据复制到系统的协议缓冲区中,由计算机发送出去。传出单元大小为1500
参数
int
WSAAPI
send(
_In_ SOCKET s, //目标socket
_In_reads_bytes_(len) const char FAR * buf,//要发送数据的缓冲区
_In_ int len,//发送的数据的字节数(超过1500会分片处理)
_In_ int flags
);
返回值:
成功 返回写入的字节数 失败返回SOCKET_ERROR
demo
int sres = send(socketClient, "发送字符串", sizeof("发送字符串"), 0);
if (SOCKET_ERROR == sres)
{
int a = WSAGetLastError();//获取错误码
printf("socket创建失败");
closesocket(socketserver);
WSACleanup();
return 0;
}