视频学习地址
1. 头文件包含:(Windows)
ws2_32.lib
的静态链接库,不然创建socket的时候回找不到对应的函数。
#include
#include
socket
第一代接口,如果相符可能会导致宏重复定义。WIN32_LEAN_AND_MEAN
(微软官方解决方案),及时文件顺序有误也没有关系:#define WIN32_LEAN_AND_MEAN
#include
#include
启动和关闭
int main()
{
WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
WSADATA dat;
WSAStartup(ver,&dat);//启动
WSAcleanup();//关闭
}
功能:建立简易的TCP服务器,监听各个客户端,只要有客户端加入就发送msgbuf里面的东西。
#define WIN32_LEAN_AND_MEAN
#define SOCKET int //VS2015已经定义好的,可以直接用
#include
#include
#include
int main()
{
WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
WSADATA dat;
WSAStartup(ver,&dat);//启动
//创建套接字
SOCKET seradd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//bind
SOCKADDR_IN _sin = {};
_sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改
_sin .sin_port = htons(1234);
_sin.sin_addr_s_un.S_addr = INADDR_ANY;//访问本机任何网络地址都可以,具体也可以改为本机的某个特定地址
if (SOCKET_ERROR == bind(seradd , (sockaddr *) &_sin , sizeof(_sin)))
{
std::cout<<"绑定失败"<<endl;
}
//监听
if (SOCKET_ERROR == listen(seradd,5))
{
std::cout<<"绑定失败"<<endl;
}
//接受
sockaddr_in clentAddr = {};//不用赋值了,因为是监听到的,不是给定的
int nAddrLen = sizeof(sockaddr_in);//给定长度
SOCKET _cSock = INVAID_SOCKET;
//发送内容需要预先说定
char msgbuf[] = "XXXXXXX";
while(true)
{
_cSock = accept(seradd,(sockaddr *) &clentAddr,nAddrLen);
if(_cSock = INVAID_SOCKET)
{
std::cout<<"获取失败"<<endl;
}
send(_cSock,msgbuf,sizeof(msgbuf)+1,0);//发送msgbuf中的内容。如果长度确定则不应该每次都重新计算
}
closesocket(seradd);
WSAcleanup();//关闭
}
int socket(
int family, //协议簇:AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY,分别是IPv4,IPv6协议,UNIX域协议,路由套接口,密钥套接口
int type,//type指定套接口类型
int protocol
);
协议簇 | 实际名字 |
---|---|
AF_INET | IPv4 (一般是这个) |
AF_INET6 | IPv6协议 |
AF_LOCAL | UNIX域协议 |
AF_ROUTE | 路由套接口 |
AF_KEY | 密钥套接口 |
套接口类型 | 实际名字 |
---|---|
SOCK_STREAM | 流套接字(一般是这个) |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字(不常用了) |
SOCK_RAW | 原始套接字 |
protocol | 实际名字 |
---|---|
IPPROTO_TCP | TCP协议(一般是这个) |
IPPROTO_UDP | UDP协议 |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd
: 就是之前socket创建时做出来的标识符或者叫做套接字。
sockaddr
在LINUX
下需要包括头文件#include
中定义,sockaddr
的缺陷是:sa_data
把目标地址和端口信息混在一起了。
所以需要使用sockaddr_in
!!
sockaddr_in
在头文件#include
或#include
中定义:
赋值:
sockaddr_in _sin = {};
_sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改
_sin .sin_port = htons(1234);
_sin.sin_addr_s_un.S_addr = INADDR_ANY;//访问本机任何网络地址都可以,具体也可以改为本机的某个特定地址
if (SOCKET_ERROR == bind(seradd , (sockaddr *) &_sin , sizeof(_sin)))
{
std::cout<<"绑定失败"<<endl;
}
int listen(int sockfd, int backlog);
backlog表示最大连接数,根据需求确定。
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
sockaddr_in clentAddr = {};//不用赋值了,因为是监听到的,不是给定的
int nAddrLen = sizeof(sockaddr_in);//给定长度
SOCKET _cSock = INVAID_SOCKET;
_cSock = accept(seradd,(sockaddr *) &clentAddr,nAddrLen);
if(_cSock = INVAID_SOCKET)
{
std::cout<<"获取失败"<<endl;
}
send(_cSock,msgbuf,sizeof(msgbuf)+1,0);//发送msgbuf中的内容。
功能:在已建立连接的套接字上发送数据.
格式:int send(SOCKET s, const char *buf, int len, int flags)。
参数:s-已建立连接的套接字;buf-存放将要发送的数据的缓冲区指针;len-发送缓冲区中的字符数;
flags-控制数据传输方式:
(1)0:接收的是正常数据,无特殊行为。
(2)MSG_DONTROUTE:表示目标主机就在本地网络中,无需路由选择。
(3)MSG_OOB:表示处理带外数据。
返回值:发送成功时返回发送的数据长度,连接结束时返回0,连接失败时返回SOCKET_ERROR。
#define WIN32_LEAN_AND_MEAN
#include
#include
int main()
{
WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
WSADATA dat;
WSAStartup(ver,&dat);//启动
SOCKET cliadd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//bind
SOCKADDR_IN _sin = {};
_sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改
_sin .sin_port = htons(4567);//和拂去其不同
_sin.sin_addr_s_un.S_addr = inet_addr("127.0.0.1");//绑定服务器地址
if (SOCKET_ERROR == connect(cliadd, (sockaddr *) &_sin , sizeof(_sin)))
{
std::cout<<"连接失败"<<endl;
}
//接受数据
char recvBuf[256] = {};//最多可以接受的字符
int nLen = recv(cliadd,recvBuf,0);
if(nLen > 0 )
{
cout<<"接受到的数据:"<<recvBuf<<endl;
}
closesocket(cliadd);
WSAcleanup();//关闭
}