1.什么是socket
socket 是开发接口.是TCP/IP网络环境下.应用程序与驱动程序之间访问的接口.
2.服务跟类型
socket服务 分为面向连接跟无连接,代表的协议就是TCP/IP
socket类型: 有三种类型
SOCK_STREAM 流式套接字. 可靠的套接字.可以处理大量数据.不会丢包.但是开销比大. 代表就是TCP协议. 是在传输层上做的.
SOCK_DGRAM 数据报套接字.不可靠.允许丢包. 常用与音频.视频等等. 代表就是UDP协议.
SOCK_RAW 原始套接字. 是在网络层进行编程的.也就是对底层的IP可以进行编程.不过常用的就是前边两种.
3.构建Windows框架.
4.IP地址的表现形式.
5.编写一个简单的网络程序. TCP模型.
在windows下使用socket需要使用windows初始化函数.还要包含库文件.
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
以及使用API进行初始化
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
WSAStartup(版本号,执行成功会返回一个WSADATA结构)
版本号使用MAKEWORD即可.我们现在使用的是2版本. 其实就是 高位低位都为2 0x0202这个值.
返回值:
0成功
1失败;
完整代码:
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data))
{
puts("对不起初始化失败\r\n");
system("pause");
}
getchar();
return 0;
}
IP地址常用点分法来表示 也就是用点隔开. 用4个 0到255之间的整数来表示
例如:
192.168.0.0.1
但是在计算机中.不使用 点分法来保存IP地址. 这样会浪费存储空间.而且.不便计算 子网掩码.
我们知道 子网掩码 跟IP地址是and的关系. 所以不使用这个.
1.网络字节序
在网络传送中,IP地址会保存为32位的二进制数.
低位存储地址中保存数据的高位字节.高位中存储低位字节. 也就是我们常说的大端模式.
小端模式:高地址存储高位,低地址存储低位
0x12345678 地址放高位. 高地址放低位 例如: 78 56 34 12 我们的高地址存储了12 依次类推.
大端模式:
高地址存放低位, 低地址存放高位.
0x 12345678 在内存中表现形式 12 34 56 78
这个就是大端模式.
VC中 in_addr 来保存IP地址. 对应的转化则是 inet_addr 跟 inet_ntoa
示例代码如下:
in_addr addr;
addr.S_un.S_addr = inet_addr("127.0.0.1"); //保存我们的IP地址.需要进行转换.
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; s_b1 ,s_b2 就是保存着我们的IP地址.点分法表示.
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
转换回来:
使用 inet_ntoa(addr)即可.
char *pszIp = NULL;
pszIp = inet_ntoa(addr);
代码调试截图:
2.主机字节顺序
什么是主机字节顺序.主机字节顺序就是指不同的主机在堆IP地址进行存储的时候.使用的格式不同.
所以需要通过函数进行转换.
htonl() 将主机字节顺序格式的IP地址转化成为TCP/IP网络字节顺序
htons 主机转网络.
ntohl 网络转主机
ntohs 网络转主机.
h 主机的意思 to 转化的意思 n 网络的意思 network l 就是 ulong
所以根据缩写就能明白什么意思.
如下图可以清楚看到顺序
有点复杂的就是服务端(主机).他要绑定本地.并且接受连接.也就是说客户端只要连接.就会有一个Socket
我们对这个socket操作.就能实现主机跟客户端的通信了.
当有多个客户端连接的时候.我们就要保存socket了. 这个以后会讲到.也就是可能会接触到各种各样的模型.
来进行处理.
1.socket 创建socket
socket (协议家族,套接字类型,连接类型)
示例代码:
socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)
返回一个SOCKET值. windows中定义的是 UINT
失败: INVALID_SOCKET
2.绑定
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(8564);//端口必须转换
addrserver.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//给定IP.如果是这个宏.则可以任何主机都可以连接
bind(hServer, (sockaddr *)&addrserver, sizeof(addrserver));
绑定的时候.我们要指明绑定的IP.以及绑定的端口.
此时我们会使用一个 sockaddr_in 的结构体.socket中其实真正的结构体是 sockaddr 但是操作不方便.
所以给了一个sockaddr_in的结构体. 大小跟 sockaddr结构体是一样的.
因为使用的是网络字节序. 所以 我们的端口以及IP地址都需要进行转换.
IP地址是 32位的.所以使用 htonl
端口是2个字节.也就是16为. 所以使用 htons
3.监听套接字
nRet = listen(hServer, 10); //参数1.socket 参数2. 同时监听处理的socket的数量
if (SOCKET_ERROR == nRet)
{
printf("listen 失败\r\n");
closesocket(hServer);
WSACleanup();
goto OPT;
}
监听就很简单了.我们要指明同时接受的socket以及同时处理的最大socket.所以给定socket 然后给一个数量即可.
4.获取用户的连接请求
获取用户的连接请求主要是用accept关键字. 给一个socket句柄.然后会把客户端连接这个socket的一系列信息都会记录在结构体中.
并且返回客户端的socket.我们只要对这个socket操作.就能进行操作了.
SOCKET hClient;
sockaddr_in addrClient; //保存客户端的addr属性.客户端的IP以及端口都会放在里面
int nLen = sizeof(addrClient);
hClient = accept(hServer, (sockaddr *)&addrClient, &nLen);传出结构.以及长度.返回客户端socket
读取的 API recv
写入的API send
相应的如果是UDP连接则使用 recvfrom /sendto
char pszBuffer[1024] = { NULL };
recv(hClient, pszBuffer, sizeof(char) * 1024, 0);
要对客户端的socket进行读写.所以传入的是客户端的socket
send是一样的.
最后进行关闭套接字即可.
服务端完整代码:
// socket.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data))
{
puts("对不起初始化失败\r\n");
system("pause");
}
in_addr addr;
addr.S_un.S_addr = inet_addr("127.0.0.1");
char *pszIp = NULL;
pszIp = inet_ntoa(addr);
SOCKET hServer;
hServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == hServer)
{
printf("socket 失败\r\n");
goto OPT;
}
//2.绑定在本地
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(8564);//端口必须转换
addrserver.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//给定IP.如果是这个宏.则可以任何主机都可以连接
int nRet = bind(hServer, (sockaddr *)&addrserver, sizeof(addrserver));
if (SOCKET_ERROR == nRet)
{
printf("bind 失败\r\n");
goto OPT;
}
//3监听套接字的连接
nRet = listen(hServer, 10); //参数1.socket 参数2. 同时监听处理的socket的数量
if (SOCKET_ERROR == nRet)
{
printf("listen 失败\r\n");
closesocket(hServer);
WSACleanup();
goto OPT;
}
//4.获取用户的数据请求.也就是接受客户端的连接的socket
SOCKET hClient;
sockaddr_in addrClient; //保存客户端的addr属性.客户端的IP以及端口都会放在里面
int nLen = sizeof(addrClient);
hClient = accept(hServer, (sockaddr *)&addrClient, &nLen);
//5.接受用户发送的数据
char pszBuffer[100] = { NULL };
recv(hClient, pszBuffer, sizeof(char) * 100, 0); //阻塞读取.不读取不返回
printf("接受到的数据 = %s \r\n", pszBuffer);
OPT:
closesocket(hClient); //释放一切资源.
closesocket(hServer);
WSACleanup();
getchar();
return 0;
}
客户端代码就很简单了.
需要用到一个 connect API. 也就是连接的API. 调用这个API则跟服务端进行连接. 服务端的accept就会返回这个socket.
完整代码.
// socket.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data))
{
puts("对不起初始化失败\r\n");
system("pause");
}
//1.创建
SOCKET hClient;
hClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == hClient)
{
printf("socket 失败\r\n");
goto OPT;
}
//2.连接
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //指定IP跟端口才能连接
addr.sin_port = htons(8564);
connect(hClient, (sockaddr *)&addr, sizeof(addr));//指明服务端信息.才能进行通讯.
//3.收发数据.
char pszBuffer[100] = "HelloWorld";
send(hClient, pszBuffer, sizeof(char) * 100,0);
OPT:
closesocket(hClient); //释放一切资源.
WSACleanup();
getchar();
return 0;
}
应用实现截图:
课堂代码:
链接:https://pan.baidu.com/s/1iKX4cvFdQ9yAnZaIydrQIg 密码:9d0t