Windows下的网络编程
一.面向连接–TCP
特性
传输过程中数据不会丢失
按照顺序传输数据
不存在数据边界
流程:
服务端代码
#include<winsock2.h>
#include<stdlib.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main() {
printf("TCP Server\n");
//初始化网络库
// 加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
// 初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (sockSrv == INVALID_SOCKET) {
printf("create socket error: %s\n",GetLastError());
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htons(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//bind
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(addrSrv)))
{
printf("bind error = %d\n", GetLastError());
}
//listen
if (SOCKET_ERROR == listen(sockSrv, 5))// 最大监听数目,执行到listen,未执行到accept//该部可以接受连接放到队列,
//但是不会处理数据
{
printf("listen error = %d\n", GetLastError());
}
printf("sleep start");
Sleep(60000);
printf("slepep end");
SOCKADDR_IN addrCil;
int len = sizeof(addrCil);
while (TRUE) {
SOCKET sockConn= accept(sockSrv, (SOCKADDR*)&addrCil, &len);
char sendBuf[100] = { 0 };
sprintf_s(sendBuf, 100, "welcome to china!");
int iLen = send(sockConn, sendBuf, strlen(sendBuf), 0);
char recvBuf[100] = { 0 };
iLen = recv(sockConn, recvBuf, 100, 0);
printf("recvBuf = %s\n", recvBuf);
closesocket(sockConn);
}
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
大型数据接收解决代码
int MyRecv(int sock, char* buf, int datasize) {
int numRecvSoFar =0 ;
int numRemainingToRecv = datasize;
printf("enter MySockRecv0\n");
while (1) {
int byteRead = recv(sock, &buf[numRecvSoFar], numRemainingToRecv, 0);
if (byteRead == numRemainingToRecv) {
return 0;
}
else if (byteRead > 0) {
numRecvSoFar += byteRead;
numRemainingToRecv -= byteRead;
}
else if ((byteRead < 0) && errno == EAGAIN) {
continue;
}
else {
return -1;
}
}
}
客户端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<winsock2.h>
#include<stdlib.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main() {
printf("TCP Client\n");
//初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
// 初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
//创建套接字
printf("create socket\n");
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockCli == INVALID_SOCKET) {
printf("create socket error: %d\n", GetLastError());
return -1;
}
//配置要连接的服务器
SOCKADDR_IN addsrv;
addsrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addsrv.sin_family = AF_INET;
addsrv.sin_port = htons(6000);
//连接服务器
printf("connect\n");
if (connect(sockCli, (SOCKADDR*)&addsrv, sizeof(addsrv)) == SOCKET_ERROR) {
printf("connect error = %d \n", GetLastError());
}
//收发数据
char sendBuf[100] = "sdfghjgfds";
printf("send\n");
int iLen = send(sockCli, (char*)sendBuf, 100, 0);
char recvBuf[100] = { 0 };
printf("recv\n");
iLen = recv(sockCli, recvBuf, 100, 0);
printf("%s",recv);
closesocket(sockCli);
WSACleanup();
printf("close\n");
system("pause");
return 0;
}
二.面向消息–UDP
特性
强调快速传输而非顺序
传输的的数据可能丢失,也可能损毁
限制传输大小
传输数据有数据边界
流程
服务端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<WinSock2.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main() {
cout << "server " << endl;
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
// 初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM,0);
if (sockSrv == INVALID_SOCKET) {
printf("create socket error: %d\n", GetLastError());
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
SOCKADDR addrCli;
int len = sizeof(SOCKADDR_IN);
char sendBuf[100] = {0};
char recvBuf[100] = { 0 };
while (true) {
//接收
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
cout <<"recv:" << recvBuf << endl;
//发送
sprintf_s(sendBuf, 100, "Ack %s", recvBuf);
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
}
closesocket(sockSrv);
return 0;
}
客户端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<WinSock2.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main() {
cout << "client " << endl;
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
// 初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
//创建套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
if (sockCli == INVALID_SOCKET) {
printf("create socket error: %d\n", GetLastError());
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
int len = sizeof(SOCKADDR_IN);
char sendBuf[100] = "hello";
char recvBuf[100] = { 0 };
//发送
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, len);
//接收
recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);
cout << recvBuf << endl;
closesocket(sockCli);
system("pause");
return 0;
}
三.零碎知识
需要引入头文件winsock2.h
导入
SOCK_STREAM----TCP
面向连接
SOCK_DGREM-----UDP
无连接
SOCK_RAW----原始套接字
可以读写内核没有处理的ip数据报,可以直接传输给需要的应用程序
常用函数
数据类型sockaddr
struct sockaddr {
u_short sa_family; //16 位地址类型 2 字节//指明ipv4还是ipv6
char sa_data[14]; //14 字节地址数据:ip + port
};
struct sockaddr_in {
short sin_family; //16 位地址类型//指明ipv4还是ipv6
u_short sin_port; //16 位端口号 65535 2 的 16 次方
struct in_addr sin_addr; //32 位 IP 地址 4 字节
char sin_zero[8]; //8 字节填充
};
大小都是16字节
sockaddr是面向操作系统的使用的
sockaddr_in是面向程序员的使用的
工具——错误查找
可以通过返回的错误编码去查找错误
10060–ip地址错误
10061–计算机拒绝访问(端口问题)
listen
if (SOCKET_ERROR == listen(sockSrv, 5))// 最大监听数目,执行到listen,未执行到accept//该部可以接受连接放到队列,
//但是不会处理数据
{
printf("listen error = %d\n", GetLastError());
}
最大监听数目,执行到listen,未执行到accept
该部可以接受连接放到队列,但是不会处理数据
第6个客户端无法连接