[TCPIP⽹络编程]使⽤C++实现简单的TCP通信(windows)⼯具:VS2015
学习前先稍微了解下系统提供的动态链接库(DLL),windows API中所有函数都包含在⾥⾯,这⾥不深⼊讲解,只需知道接下来的socket编程要⽤到⾥⾯的各种函数就⾏了,⼀般来说我们能学会调⽤就已经⾜够了,对应的头⽂件为 winsock2.h。
使⽤DLL前必须把DLL加载到当前程序,加载⽅式分为动态加载和静态加载两种,这⾥我使⽤静态加载做⽰范,需要⽤到 #pragma 命令,可以在编译时加载,形式为: #pragma comment(lib,"ws2_32.lib"),这是告诉编译器要加载⼀个库,⽽这个库的名字叫
做“ws2_32.lib”。那么我们加载完了就可以直接使⽤了吗?答案时否定了,我们还需要对其进⾏初始化。初始化需要⽤到⼀个函数
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData)。wVersionRequested 为 WinSock 规范的版本
号,lpWSAData 为指向WSADATA结构体的指针。这⾥我们使⽤得版本号为2.2,于是这样写WSAStartup(MAKEWORD(2, 2),
&wsadata);函数第⼀个参数类型为WORD,等价于unsigned short ,所以需要⽤MAKEWORD()进⾏转
换,接下来我们定义⼀个WSADATA 指针放在第⼆位就搞定了。以上的代码总结为以下
#include<iostream>
#include<string>
#include<winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main()
{
//初始化DLL
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
return 0;
}
因为我们进⾏的是TCP通信,所以要⽤到的传输⽅式为SOCK_STREAM,表⽰⾯向连接的可靠传输,对应得UDP使⽤的传输⽅式为SOCK_DGRAM,表⽰⽆连接的不可靠传输。先给出⼀个服务器端的代码,再⼀⼀分析。
//服务器端.cpp
#include<iostream>
#include<string>
#include<winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
SOCKET serSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //⽤0填充每⼀个字节
sockAddr.sin_family = PF_INET; //使⽤IPv4地址
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的ip地址
sockAddr.sin_port = htons(1234); //具体的端⼝号
//绑定套接字
bind(serSock, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
listen(serSock, 20);
return 0;
}
先看SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);AF_INET代表使⽤IPv4地址,对应的IPv6⽤AF_INET6表⽰,AF是“Address Family”的缩写,INET是“intenet”的缩写;第⼆个参数SOCK_STREAM代表传输的⽅式是以字节流的形式;最后⼀个参数表⽰使⽤TCP协议。整个函数返回的是⼀个服务器端的套接字,⽤serSock表⽰。
接下来⼏⾏的功能是设置服务器的相关信息,然后使⽤bind函数将套接字serSock和设置的各种属性绑定,⽐较需要注意的是第⼆个参数,需要将sockaddr_in转化为SOCKADDR,这是因为这两个结构体的字节数相同,相互转化不会导致数据得丢失,其次,sockaddr已经将ip地址和端⼝号合并在⼀起,想要对两个进⾏赋值会相当⿇烦,⽽sockaddr_in则把这两个分开,⽅便赋值,之后再强转化。最后通
过listen函数进⾏监听,第⼆个参数表⽰请求队列的数量,具体多少根据情况设置。
完整的服务器端cpp
#include<iostream>
#include<string>
#include<winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
const int BUF_SIZE = 100;
int main()
{
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
SOCKET serSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //⽤0填充每⼀个字节
sockAddr.sin_family = PF_INET; //使⽤IPv4地址
socket编程聊天室基本流程sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的ip地址
sockAddr.sin_port = htons(1234); //具体的端⼝号
//绑定套接字
bind(serSock, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
listen(serSock, 20);
//接受客户端请求
SOCKADDR clientAddr;
int clientAddr_size = sizeof(clientAddr);
SOCKET clientSock = accept(serSock, (SOCKADDR*)&clientAddr, &clientAddr_size);
/
/向客户端发送消息
char* str = "hello world!!";
send(clientSock, str, strlen(str)+sizeof(char),NULL);
//关闭套接字
closesocket(clientSock);
closesocket(serSock);
WSACleanup();
system("pause");
return 0;
}
accept()函数:当套接字处于监听状态,可以通过accept()接受客户端的请求。
send()函数:向客户端传输内容。
客户端跟服务端差不多
#include<iostream>
#include<string>
#include<stdio.h>
#include<winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
const int BUF_SIZE = 100;
int main()
{
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
//需要连接服务端的信息
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
//创建套接字并连接
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
connect(sock, (SOCKADDR*)& sockAddr, sizeof(sockAddr));
char infoBuff[MAXBYTE] = { 0 };
recv(sock, infoBuff, MAXBYTE,NULL);
cout << "从服务器接受到得信息为:" << infoBuff << endl;
closesocket(sock);
WSACleanup();
system("pause");
return 0;
}
recv()函数,从服务端接受传来得信息并存在infoBuff数组⾥。
先运⾏服务端的cpp,此时服务端进⼊监听状态,再运⾏客户端cpp,便可在控制台上打印出服务端传来的⽂字。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论