Lesson10:網絡編程基礎
網絡編程是計算機編程的一個重要分支,是網絡通信的基礎,網絡編程主要用到了Windows系統系統提供的socket。網絡通信又分爲基於TCP和基於UDP兩種。本文主要講解基於TCP和UDP的網絡編程基礎知識。
1. 基於TCP的socket編程
1.1 TCP服務器
#include <winsock2.h>
#include <stdio.h>
//#include <iostream>
#pragma comment(lib,"ws2_32.lib") //靜態加入一個lib文件,也就是庫文件ws2_32.lib文件,它提供了對以下網絡相關API的支持
void main()
{
/******** 首先設定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //請求的通信版本爲(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //調用WSAStartup函數
if(err != 0)
return; //如果返回值不爲0,表示沒有對應的WinSockDLL.則返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字節不爲1,低字節不爲1,則返回。
return;
}
//1創建套接字
SOCKETsockSrv = socket(AF_INET, SOCK_STREAM,0); //(1地址族AF_INET 或 PF_INET,2socket類型SOCK_STREAM或 SOCK_DGRAM,3與特定的地址家族相關的協議) 調用成功返回新的socket數據類型的套接字描述符
//2綁定套接字到本地地址和端口
/*structsockaddr_in{
short sin_family; //地址族
unsignedshort sin_port; //端口號
struct in_addr sin_addr; //套接字的主機IP地址
char sin_zero[8]; //填充數,使sockaddr_in和sockaddr結構長度一樣
}*/
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY); //調用htonl函數進行轉換主機字節序爲TCP/IP網絡字節序//addrSrv是一個結構體,裏面有一個結構體變量sin_addr,裏面包含共用體S_un,然後是共用體成員S_addr,inet_addr函數進行主機字節序轉換爲網絡字節序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port=htons(6000); //端口號爲6000,需要1024以上端口號,網絡字節序,需要轉換,htonl和htons不同在於轉換數據的範圍
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //綁定套接字和地址的 函數 (1指定要綁定的套接字,2該套接字的本地地址信息,是指向sockaddr結構的指針變量,3指定該地址的長度)
//3調用listen函數,將套接字設置爲監聽模式,準備接受客戶請求
listen(sockSrv,5); //(套接字描述符,最大的等待連接隊列數)
//4等待客戶請求到來
//5用返回的套接字和客戶端進行通信
//6返回,等待另一個客戶請求
//7關閉套接字
SOCKADDR_INaddrClient; //定義一個地址結構的變量,用來接受客戶端的地址信息,在循環結構中用到
intlen = sizeof(SOCKADDR);
while(1) //死循環,一直監聽,持續運行
{
SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); //(1處於監聽狀態的主機服務器套接字描述符,2指向buffer指針連接實體地址,保存了發起客戶端的ip信息和端口信息,3指向整型指針,包含返回地址結構的長度),返回一個新的連接的套接字描述符,用他和客戶端通信,先前的套接字繼續監聽客戶連接請求
//發送數據
/*intsend(
SOCKETs, //建立連接所對應的套接字描述符,不是監聽的套接字
constchar FAR * buf, //一個buff指針,包含將要傳送的數據
int len, //buffer中數據的長度
intflags //設定send的調用行爲
);*/
charsendBuf[100];
sprintf_s(sendBuf,"Welcome%s to http://www.sunxin.org",inet_ntoa(addrClient.sin_addr)); //把格式化的數據寫入某個字符串,將客戶端地址傳給字符串裏的%s,
send(sockConn,sendBuf, strlen(sendBuf)+1, 0);
//接受數據
/*intrecv( The Windows Socketsrecv function receives data from a connected socket.
SOCKETs, //建立連接的套接字
charFAR* buf, //接受數據的buff
intlen, //buff的長度
intflags //設定接受函數的調用行爲
);*/
charrecvBuf[100];
recv(sockConn,recvBuf, strlen(recvBuf) + 1, 0);
printf("%s\n",recvBuf);
closesocket(sockConn);
}
}
1.2 TCP客戶端
#include <winsock2.h>
#include <stdio.h>
//#include <iostream>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先設定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //請求的通信版本爲(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //調用WSAStartup函數
if(err != 0)
return; //如果返回值不爲0,表示沒有對應的WinSockDLL.則返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字節不爲1,低字節不爲1,則返回。
return;
}
//1創建套接字
SOCKETsockClient= socket(AF_INET, SOCK_STREAM, 0); //(1地址族AF_INET 或 PF_INET,2socket類型SOCK_STREAM或 SOCK_DGRAM,3與特定的地址家族相關的協議) 調用成功返回新的socket數據類型的套接字描述符
/*intconnect( //The Windows Socketsconnect function establishes a connection to a specifed socket.
SOCKETs, //套接字
conststruct sockaddr FAR* name, //地址結構體指針,用來設定鏈接的服務器地址信息
intnamelen //地址結構體的長度
);*/
//2連接服務器
SOCKADDR_INaddrSrv; //SOCKADDR_IN是一個結構體變量
addrSrv.sin_addr.S_un.S_addr= inet_addr("127.0.0.1"); //addrSrv是一個結構體,裏面有一個結構體變量sin_addr,裏面包含共用體S_un,然後是共用體成員S_addr,inet_addr函數進行主機字節序轉換爲網絡字節序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000); //主機字節序轉換爲網絡字節序
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//3和服務器端口進行通信(接受服務器發送的信息)
charrecvBuf[100];
recv(sockClient,recvBuf, strlen(recvBuf) + 1, 0);
printf("%s\n",recvBuf);
send(sockClient,"This is zhangsan", strlen("This is zhangsan") + 1, 0);
//4關閉套接字
closesocket(sockClient);
WSACleanup(); //終止對套接字庫的使用
system("pause"); //用於防止閃退
}
2. 基於UDP的socket編程
2.1 UDP服務器
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先設定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //請求的通信版本爲(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //調用WSAStartup函數
if(err != 0)
return; //如果返回值不爲0,表示沒有對應的WinSockDLL.則返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字節不爲1,低字節不爲1,則返回。
return;
}
//1創建套接字
SOCKETsockSrv = socket(AF_INET, SOCK_DGRAM, 0); //(1地址族AF_INET 或 PF_INET,2socket類型SOCK_STREAM或 SOCK_DGRAM,3與特定的地址家族相關的協議) 調用成功返回新的socket數據類型的套接字描述符
//2綁定套接字到本地地址和端口
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY); //調用htonl函數進行轉換主機字節序爲TCP/IP網絡字節序
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000); //端口號爲6000,1024以上端口號,需要網絡字節序,需要轉換,htonl和htons不同在於轉換數據的範圍
bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); //綁定函數 (1指定要綁定的套接字,2該套接字的本地地址信息,是指向sockaddr結構的指針變量,3指定該地址的長度)
/*intrecvfrom( The Windows Socketsrecvfrom function receives a datagram and stores the source address.
SOCKETs, //套接字
charFAR* buf, //接收數據的buffer
int len, //buffer的長度
int flags, //設定recvfrom函數的調用行爲
structsockaddr FAR* from, //地址結構體指針,接收發送數據方的地址信息
intFAR* fromlen //函數返回值,返回地址結構的大小
);*/
//3等待接收數據recvfrom
SOCKADDR_INaddrClient; //定義一個地址結構的變量,用來接受客戶端的地址信息,在循環結構中用到
intlen = sizeof(SOCKADDR);
charrecvBuf[100];
recvfrom(sockSrv,recvBuf, strlen(recvBuf) + 1, 0, (SOCKADDR*)&addrClient, &len);
printf("%s\n",recvBuf);
//4關閉套接字
closesocket(sockSrv);
WSACleanup(); //終止對套接字庫的使用
system("pause"); //用於防止閃退
}
2.2 UDP客戶端
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/******** 首先設定通信版本 **************/
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested= MAKEWORD(1, 1); //請求的通信版本爲(1,1)
err= WSAStartup(wVersionRequested, &wsaData); //調用WSAStartup函數
if(err != 0)
return; //如果返回值不爲0,表示沒有對應的WinSockDLL.則返回
if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //如果版本的高字節不爲1,低字節不爲1,則返回。
return;
}
//1創建套接字
SOCKETsockClient = socket(AF_INET, SOCK_DGRAM, 0); //(1地址族AF_INET 或 PF_INET,2socket類型SOCK_STREAM或 SOCK_DGRAM,3與特定的地址家族相關的協議) 調用成功返回新的socket數據類型的套接字描述符
/*intsendto( The Windows Sockets sendtofunction sends data to a specific destination.
SOCKETs, //套接字
constchar FAR * buf, //包含將要發送的數據
int len, //數據的長度
intflags, //設置sendto函數調用行爲
conststruct sockaddr FAR * to, //地址結構體指針,設定目的套接字的地址信息
inttolen //地址結構體的長度
);*/
//2向服務器發送數據(sendto)
SOCKADDR_INaddrSrv;
addrSrv.sin_addr.S_un.S_addr= inet_addr("127.0.0.1");
addrSrv.sin_family= AF_INET;
addrSrv.sin_port= htons(6000);
sendto(sockClient,"Hello", strlen("Hello") + 1, 0, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//3關閉套接字
closesocket(sockClient);
WSACleanup(); //終止對套接字庫的使用
system("pause"); //用於防止閃退
}