Socket编程基础——面向连接TCP

WinSock是Windows环境下的网络编程接口,它最初是基于Unix环境下的BSD Socket,是一个与网络协议无关的编程接口。WinSock包含两个主要版本,即WinSock1和WinSock2,在vs2010环境下,通常使用WinSock 2.2实现网络通信的功能。

1、Socket接口启动
需要引入头文件winsock2.h及库文件ws2_32.lib

#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
WSADATA wsadata;
if(WSAStartup(MAKEWORD(2,2),&wsadata)!=0)
{
    cout<<"Init error"<<endl;
    return -1;
}

WSAStartup函数原型如下:
int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData)
(1)wVersionRequested:Socket接口版本号,例如2.2版本(MAKEWORD(2,2)),高位字节存储副版本号,低位字节存储主版本号;
(2)lpWSAData:指向WSADATA结构体的指针,WSADATA结构体返回Socket信息;
如果上面函数执行成功,则返回0,否则可以通过WSAGetLastError()查看错误代码。例如:WSAEINVAL表示指定的Windows Socket版本不被该DLL支持。

2、IP地址的表示形式
对普通用户而言,IP地址使用点分法表示("192,168.0.1"字符串),但在计算机中不会使用这种方式,因为这样会浪费存储空间。实际上,计算机使用长整型存储IP地址,分为网络字节序和主机字节序。
(1)网络字节序
在网络传输过程中,IP地址保存为32位二进制,TCP/IP规定,在低位存储地址中保存数据的高位字节,这种存储顺序称为网络字节顺序,所以数据的传输由高位至低位进行的。不同网络设备和操作系统在发送数据之前都需要将二进制数据转为网络字节序。
(2)IP地址结构体
通用结构

struct sockaddr { 
unsigned short sa_family; 
char sa_data[14]; 
};

常用结构

struct sockaddr_in { 
short int sin_family; 
unsigned short int sin_port; 
struct in_addr sin_addr; 
unsigned char sin_zero[8]; 
}; 

struct in_addr就是32位IP地址。

struct in_addr { 
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr; 
} S_un;
#define s_addr  S_un.S_addr
}; 

使用inet_addrinet_ntoa这两个函数可以实现点分IP地址字符串和网络字节序IP地址之间的转换。
unsigned long inet_addr(const char *cp):点分字符串->s_addr
char FAR * inet_ntoa(struct in_addr in)in_addr(sin_addr)->点分字符串

(3)主机字节序
可以使用htonl(),htons(),ntohl(),ntohs()这四个函数来实现主机字节顺序和网络字节顺序的转换。

3、服务器端通信流程
(1)创建服务器套接字

server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

指定IP地址族、SOCKET类型,协议,返回SOCKET

(2)绑定本地端口、IP

sockaddr_in sAddr;
sAddr.sin_family=AF_INET;
sAddr.sin_port=htons(9000);
sAddr.sin_addr.S_un.S_addr=htonl(ADDR_ANY);
bind(server,(sockaddr*)&sAddr,sizeof(sAddr));

(3)监听等待连接

listen(server,5);

(4)接收连接,返回Socket

sockaddr_in cAddr;
int len=sizeof(cAddr);
client=accept(server,(sockaddr*)&cAddr,&len);

(5)接收数据

ZeroMemory(buf,BUF_SIZE);
recv(client,buf,BUF_SIZE,0);

buf为缓冲区,BUF_SIZE是缓冲区的大小;ZeroMemory函数将缓冲区置0,替代memset函数。

(6)发送数据
send(client,buf,strlen(buf),0);
在接收过程中接收大小写成缓冲区大小,发送时写成发送的数据大小!

4、客户端通信流程
(1)创建客户端套接字

client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

(2)连接服务器

cAddr.sin_port=htons(9000);
cAddr.sin_family=AF_INET;
cAddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
retVal=connect(client,(sockaddr*)&cAddr,sizeof(cAddr));

(3)发送数据/接收数据
同服务器端

5、清理
(1)shutdown禁止制定的Socket上发送和接收数据
int shutdown(SOCKET s,int how)
参数s表示要关闭的Socket,当参数how被设置为SD_RECEIVE时,不允许再次调用recv接收数据,设置为SD_SEND时,不允许再次调用send发送数据,设置为SD_BOTH时,不允许发送和接受数据。
(2)closesocket
(3)WSACleanup


角落里的阳光
PHP Javascript Node.js

Golang/PHP/Python

141 声望
25 粉丝
0 条评论
推荐阅读
求贤若渴-蚂蚁金服招聘devops高级开发工程师/技术专家
蚂蚁研发效能部是服务于整个蚂蚁金服集团的核心技术团队,打造了世界领先的金融级一体化智能研发效能平台,致力于不断提高组织研发效率和整体效能,并将服务与产品输出到金融云企业和客户。

角落里的阳光阅读 2.4k

微信小程序之聊天室(多人聊天室)总结
实现方式一:使用nodejs + socket.io实现缺点:引用weapp.socket.io.js 大小100kb实现方式二:使用小程序云开发- 数据库实时监听 来实现缺点:目前不可以跨端,只能在当前小程序聊天预览效果:未完待续..来源:[...

jigsaw1阅读 4.9k

TCP的三次握手和四次挥手
上面有一篇专门介绍过TCP和UCP协议,其中只是粗略的提到TCP协议的三次握手,而四次挥手完全没有说到,所以这次专门总结了这篇文章,专门讲讲三次握手和四次挥手。

HerryLo1阅读 1.7k

Qt中使用TCP接收报文
假设有一个TCP服务端,会向连接到它的TCP客户端周期(或随机)发送一个报文。报文由定长的报文头和不定长的报文体(数据部分)组成,报文体是一张图片,每个字节表示图片中一个像素的灰度值。我们的任务就是读取...

manxisuo阅读 627

从序号和确认号理解TCP三次握手
序号(Sequence Number):当前TCP数据部分的第一个字节编号(实际是一个非常大的值,非常大的值 - 固定值 = 小的编号,同一请求有一个固定值,固定值来源于建立连接时seq=0时)

一颗冰淇淋阅读 375

网络编程与通信原理
总感觉这个概念,和研发有点脱节;一、基础概念不同设备之间通过网络进行数据传输,并且基于通用的网络协议作为多种设备的兼容标准,称为网络通信;以C/S架构来看,在一次请求当中,客户端和服务端进行数据传输的...

知了一笑阅读 305

封面图
传输层之UDP与TCP的首部
传输层位于应用层和数据链路层之间,主要有两个协议,用户数据报协议UDP(User Datagram Protocol)、传输控制协议TCP(Transmission Control Protocol)。

一颗冰淇淋阅读 222

Golang/PHP/Python

141 声望
25 粉丝
宣传栏