网络编程
实现两台或多台已经联网的计算机互相交换数据的行为,就是网络编程.
我们日常使用的操作系统已经为我们提供了socket, 不需要熟悉网络数据传输的原理,
也能掌握网络编程.
windows中的socket与Linux中的有何区别?
- 相同
Linux通常会用文件描述符来表示或区分已经打开了的文件;windows通过文件句柄的方式来表示,和上述的Linux的文件描述符是类似的概念;- 不同
Linux的一切都是文件,所以网路连接也是一个文件.Windows则会将socket和文件区分开来,因此在Windows中socket有针对性设计的数据传输函数.
socket的类型
- SOCK_STREAM
面向连接的套接字(Stream Sockets),是对SOCK_STREAM的说明.
使用了TCP协议,有自己的纠错机制.
浏览器使用的http协议就是基于面向连接的套接字
- SOCK_DGRAM
无连接的套接字(Datagram Sockets),也叫数据报格式套接字.
它的传输效率相对于SOCK_STREAM要高,但对数据的校验工作较少.
视频聊天和语音视频大多是采用无连接套接字来传输数据的.
- SOCK_RAW
原始套接字(raw-protocol interface).
保存了数据包的完整IP头,可以通过它来对数据进行分析.
网络安全产品通常使用此类型,如MAC地址扫描器,网络嗅探器等产品.
TCP套接字

- 服务端
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &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, 0);
//准备绑定的信息
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));
//监听
listen(sockSrv, 10);
cout << "server start at prot: 6000" << endl;
SOCKADDR addrCli;
int len = sizeof(SOCKADDR);
char recvBuf[100];
char sendBuf[100];
while (true)
{
//接受连接请求
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
sprintf_s(sendBuf,100, "hello");
send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
//接受或发送数据
recv(sockConn, recvBuf, 100, 0);
std::cout << recvBuf << std::endl;
//关闭套接字
closesocket(sockConn);
}
//关闭套接字
closesocket(sockSrv);
//清理套接字库
WSACleanup();
system("pause");
return 0;
}
- 客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
//检查
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
//清理套接字库
WSACleanup();
return -1;
}
//创建tcp套接字
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.0.131");
//addrSrv.sin_addr.S_un.S_addr = htons(6000);//大失误
addrSrv.sin_port = htons(6000);
addrSrv.sin_family = AF_INET;
//连接服务器
connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
char sendBuf[] = "world";
char recvBuf[100];
//发送数据到服务器
send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
//接收服务器发送的数据
recv(sockCli, recvBuf, sizeof(recvBuf), 0);
cout << recvBuf << endl;
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}
UDP套接字

- 服务端
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
wVersion = MAKEWORD(1, 1);
int err = WSAStartup(wVersion, &wsaData);
//检查
if (err != 0)
{
cout << WSAGetLastError() << endl;
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
cout << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_port = htons(6002);//主机字节序转换
addrSrv.sin_family = AF_INET;
//绑定到本机6002端口
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//接受请求,处理请求
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
char sendBuf[] = "UDP Server ...";
char recvBuf[100];
cout << "Start UDP Server with port 6002 " << endl;;
while (true)
{
//接收数据
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
cout << "Recv: " << recvBuf << endl;
//发送数据
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
cout << "Send: " << sendBuf << endl;
}
//关闭套接字并清除套接字库
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
- 客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
//初始化套接字库
WORD wVersion;
WSADATA wsaData;
wVersion = MAKEWORD(1, 1);
int err = WSAStartup(wVersion, &wsaData);
//检查
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
//创建UDP套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_port = htons(6002);
addrSrv.sin_family = AF_INET;
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
char sendBuf[] = "send from UDP client ...";
char recvBuf[100];
//发送数据到服务端并打印
cout << "send to server: " << sendBuf << endl;
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, len);
//接收服务端数据并打印
recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
cout << "receve from: " << recvBuf << endl;
//关闭套接字并清除套接字库
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}
常用函数分析
- WSAStartup()函数
是
Windows操作系统特有的函数.
在Windows系统中使用网络编程需要加载ws2_32.dll动态链接库.
使用这个dll之前需要调用WSAStartup()函数初始化动态库.
函数原型:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
- socket()函数
程序使用
socket函数来创建套接字,不管是Linux还是Windows都一样,区别是返回值不同.
Linux中socket函数的返回值是int,Windows里的返回值是SOCKET.
我这里练习的环境是windows,所以这里的代码例子都以Windows为准.
函数原型:
SOCKET socket(int af, int type, int protocol);
- bind()函数
创建完套接字之后需要确定套接字的各种属性,比如
IP地址,端口等信息.
这些信息用一个sockaddr结构体变量存放.
函数原型:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);
- connect()函数
是客户端在创建完套接字后,用来建立连接的.
它的参数和bind相同.
函数原型:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
- listen()函数
在服务端绑定完套接字之后,还使用
listen函数让套接字进入被动监听状态.
第二个参数是请求队列的最大长度.
函数原型:
int listen(SOCKET sock, int backlog);
- accept()函数
套接字通过
listen函数进入被动监听状态之后,通过accept函数接收客户端请求.
它的参数跟listen和connect函数相同.
函数原型:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
- send()函数
经过上面一系列的连接的目的,自然是为了实现服务端与客户端之间的发送和接收数据.
send函数是从服务端发送数据,第四个参数可以参考send recv函数中的flags参数
函数原型:
int send(SOCKET sock, const char *buf, int len, int flags);
- recv()函数
recv函数的功能是从服务端或客户端接收数据.
它和send函数的参数是一样的.
函数原型:
int recv(SOCKET sock, char *buf, int len, int flags);
总结
这篇博客内容只是简单的涵盖了socket编程需要用到的函数,
以及给出了简单的示例代码.对于socket编程入门还是有帮助的.\
Windows操作系统拓展的套接字函数一般以WSA开头,它的意思是Windows socket API.常用的比如:
WSASend,WSARecv以及前面提到的WSACleanup等.
本文详细比较了Windows和Linux中socket的异同,并通过TCP和UDP套接字实例展示了服务器端与客户端的编程。重点讲解了WSAStartup、socket、bind、connect、listen和accept等关键函数的用法及其在网络编程中的作用。


被折叠的 条评论
为什么被折叠?



