Socket 编程详解
1.什么是Socket?
Socket = 套接字 , 它是计算机之间进行通信的一种约定或一种方式。
通过 socket 这种约定 , 一台计算机可以接收其他计算机的数据 , 也可以向其他计算机发送数据。
2.为什么要使用Socket?
计算机之间进行网络通讯的工作为 7 层(OSI模型) :
从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
而后来因为复杂性而简化为 4 层(TCP/IP 模型) :
从下到上分别是接口层、网络层、传输层和应用层。
我们所说的 socket 编程 , 是站在传输层的基础上 , 所以可以使用 TCP/UDP 协议经由网络模型的封装后实现在网络中发送数据。
3.怎么使用Socket?
3.1 server基础语法(windows)
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32")
int main(){
// 初始化winsock2 Dll , 此处第一个参数对应socket的版本号2.2 , 第二次参数对应的是Socket细节
WSADATA wsData;
WSAStartup(MAKEWORD(2, 2), &wsData);
// 创捷套接字 , 第一个参数对应的是IP协议 , 第二个则是套接字类型 , 第三个为特定协议(填0系统会自动判断)
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 设置地址 , 绑定套接字 , 用于接下来的数据监听
sockaddr_in sAddr;
memset(&sAddr, 0, sizeof(sAddr)); //每个字节都用0填充
serAddr.sin_family = AF_INET;/* 对应上面分AF_INET , 代表的是使用ipv4 */
serAddr.sin_port = htons(/* server开放的端口号 */);
serAddr.sin_addr.S_un.S_addr = inet_addr(/* server的IP地址 */);
bind(sock, (struct sockaddr*) & sAddr, sizeof(sAddr));
// 监听socket , 第一个参数对应需要被监听的sock , 此处的sock对应的是server的地址信息 ,
// 第二个参数是数据队列最多可以容纳多少个客户端排队连接
listen(sock , 20);
// 响应连接请求 , 函数accept()如果没有连接将会阻塞
struct sockaddr cAddr;
int cSize = sizeof(struct sockaddr);
SOCKET clientSock = accept(sock, (struct sockaddr*) & cAddr, &cSize);
// 发送数据给客户端 , 其实send是将数据放入TCP的发送缓冲中 , 具体发送时间由TCP协议自己控制
char * str = "Hello Socket";
send(clientSock, str, strlen(str)+1, NULL);
// 发送后关闭套接字
closesocket(clientSock);
closesocket(sock);
// 停止Dll
WSACleanup();
return 0;
}
3.1 client基础语法(windows)
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32")
int main(){
// 初始化winsock2 Dll , 此处第一个参数对应socket的版本号2.2 , 第二次参数对应的是Socket细节
WSADATA wsData;
WSAStartup(MAKEWORD(2, 2), &wsData);
// 创捷套接字 , 第一个参数对应的是IP协议 , 第二个则是套接字类型 , 第三个为特定协议(填0系统会自动判断)
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 设置地址 , 绑定套接字 , 准备进行连接
sockaddr_in sAddr;
memset(&sAddr, 0, sizeof(sAddr)); //每个字节都用0填充
serAddr.sin_family = AF_INET;/* 对应上面分AF_INET , 代表的是使用ipv4 */
serAddr.sin_port = htons(/* server开放的端口号 */);
serAddr.sin_addr.S_un.S_addr = inet_addr(/* server的IP地址 */);
// 进行连接 , 此时服务器的accept端口将取消阻塞向下执行
connect(sock, (struct sockaddr*) & sAddr, sizeof(sAddr));
// 接收服务器传回的数据 , 函数accept()如果没有连接将会阻塞
char strBuf[100] = { 0 };
recv(sock, strBuf, 100, NULL);
printf(" 服务器传入的数据是 : %s \n",strBuf);
// ** 此处数据结果是 "服务器传入的数据是 : Hello Socket" **
// 接收后后关闭套接字
closesocket(sock);
// 停止Dll
WSACleanup();
system("pause");
return 0;
}
总结
上面描述了最基础的windows下socket的编程方法 , 如果不好理解你可以看看我在下面描述的比喻.
TCP的连接方式是持续的 , 你可以将其理解为高铁的运输 , 而你是这班高铁的站长 , 你要传输的数据就是你的乘客
其一物理链接是高铁的轨道 , 没有轨道一切都免谈 ; 并且轨道的质量将决定你车的速度 , 就算你是最好的车 , 开到一半轨道断了也没办法继续跑了 , 只能换个轨道重新跑 ;
第二TCP协议就是你的车 , 没有TCP协议就算有轨道乘客也没办法从你这里到达目标地点. 如果你用错了协议 , 比如上面的程序 , 你使用了UDP , 如果你的目标地点不支持UDP这辆车你的乘客也没法到达目的地.
那么现在你有了铁轨 , 有了车 , 想要让数据上车或者下车 , 那么就需要岳台 , 岳台就是TCP的缓冲区 , 这个缓冲区呢有自己的安保人员 , 负责你乘客的上下车 , 而你只需要告诉乘客去哪个岳台即可.
最后你的乘客上了车 , 到达了目标地点 , 目标地点也同样识别TCP这辆车 , 那乘客就可以成功的下车了.