复习一下,先复习下简单的套接字。
好记性不如烂笔头,烂笔头不如烂键盘,烂键盘好过没键盘。
Windows下.........................................................................................................................
类型:
流式套接字(SOCK_STREAM):套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。TCP
数据报式套接字(SOCK_DGRAM):提供无连接服务,数据包以独立包形式发送,不提供无措保证,数据可能丢失或重复,并且接收顺序混乱。UDP
原始套接字(SOCK_RAW):原始套接字与流式和数据报套接字的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接
常用基本函数:
- int WSAStartup(WORD,& WSADATA)
说明:加载套接字库,加载成功返回0。
参数一:指定套接字版本,高位指定主版本,低位指定副版本,可以利用MAKEWORD(x,y)生成,其中x是高位,y是低位。
参数二:用于接收WSAStartup加载的套接字库版本信息。WSADATA结构体中,第一个成员代表主版本,第二个成员代表副版本。
- int socket(int af, int type, int protocol)
说明:创建套接字。成功返回套接字描述符。失败返回INVALID_SOCKET。
参数一:指定地址族,对于TCP/IP协议 ,只能为AF_INET也可以写为PF_INET.
参数二:指定套接字类型,SOCK_STREAM为流式套接字(TCP),SOCK_DGRAM产生数据报套接字(UDP)。
参数三:指定与地址家族相关的协议,如果为0会自动分配一个合适的协议。
- int bind( SOCKET sockaddr, const struct sockaddr FAR* my_addr,int addrlen)
说明:为套接字绑定本地地址,成功返回0,失败返回-1
参数一:通过socket()函数创建的套接字
参数二:指定的本地地址信息,类型为sockaddr结构指针变量
参数三:指定地址结构的长度,sizeof(sockaddr)
struct sockaddr { unsigned short sa_family; //指定地址族,必须为AF_INET char sa_data[14]; //占位,指定协议相关具体地址信息 };
为了方便地址填写,一般用sockaddr_in代替sockaddr,参数填写的时候进行强转即可
struct sockaddr_in { short int sin_family; //地址族 只能是AF_INET unsigned short int sin_port; //指定端口号(要网字节序 使用函数htons()转化) struct in_addr sin_addr; //套接字主机IP地址,用到in_addr结构体 unsigned char sin_zero[8]; //填充数,使sockaddr_in和sockaddr的sizeof相等 };
对于IP地址,如果指定为INADDR_ANY,则允许套接字向任何分配给本机机器的IP地址发送或接受数据,如果机器有多个网卡,可以简化应用程序的编写。
typedef struct in_addr { union { struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b; struct{ unsigned short s_w1, s_w2;} S_un_w; unsigned long S_addr; // IP地址(网络字节序,利用htonl()转换) } S_un; } IN_ADDR;
inet_addr(“192.168.1.156”)会返回一个适合S_addr 的unsigned long类型,inet_ntoa(in_addr)则相反 ,会将一个in_addr结构体返回一个点分的字符串,”192.168.1.156”
- int listen(int sockfd, int backlog)
说明:将套接字设置为监听模式,成功返回0,失败返回-1
参数一:套接字描述符socket()函数创建的套接字
参数而:等待连接队列的最大长度(并不是在一个端口连接数)
- accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
说明:等待客户连接,并接受客户连接。请求成功,返回一个相对于连接的新的套接字,原先的套接字,继续监听连接请求,失败返回INVALID_SOCKET
参数一:套接字描述符
参数二:保存发起连接的套接字的IP和端口信息,也利用sockaddr_in代替sockaddr
参数三:保存返回的套接字地址长度,必须要赋初值,sizeof(sockaddr)
- int connect (SOCKET s, const struct sockaddr * name, int namelen )
说明:与指定套接字建立连接,成功返回0,失败返回SOCKET_ERROR,也就是-1.
参数一:套接字描述符
参数二:指定要连接的套接字的地址,也就是服务端的
参数三:地址长度
- int send(SOCKET s, const char FAR *buf, int len, int flags) / sendto
说明:向指定套接字发送数据,send用于TCP连接,sento用于UDP连接,并且sendto多了两个参数,用来指定服务端的地址和长度。成功返回发送数据长度,失败返回SOCKET_ERROR
参数一:指定接收端套接字,即通过accept()接受的套接字
参数二:要发送的字符数据
参数三:带发送数据的长度
参数四:执行方式,影响send的调用行为,一般设为0
- int recv(SOCKET s, char FAR *buf, int len, int flags) / recvfrom
说明:接收指定套接字数据,recv用于TCP连接,recvfrom用于UDP连接,并且recvfrom多了两个参数,用来指定服务端的地址和长度,并且接收地址长度的参数必须初始化。成功返回接收数据长度,失败返回-1
参数一:指定接收端的套接字
参数二:存储数据的缓冲区
参数三:缓冲区大小
参数四:执行方式
- closesocket(socket)
说明:关闭套接字
- WSACleanup()
说明:终止对套接字库的使用
- int WSAGetLastError() / GetLastError()
说明:返回一个int类型的Winsock错误代码,对于上述函数执行返回失败,都可以利用该函数进行捕捉错误。
TCP和UDP连接:
TCP:面向连接的,可靠的
(1)数据传输过程必须经过建立连接、维护连接和释放连接3个阶段
(2)在传输过程中,不需要携带目的主机的地址
(3)可靠,协议复杂,通信效率不低
UDP:面向无连接的,不可靠
(1)不需要连接
(2)每个分组都携带完整的目的主机地址,在系统中独立传送
(3)没有顺序控制,接收方的分组可能出现乱序、重复和丢失现象
(4)通信效率高,不可靠
TCP例子:
图片 哪儿来的?
服务端:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<winsock2.h>
using namespace std;
int main()
{
WORD word = MAKEWORD(2, 0);
WSADATA wsaData;
if (WSAStartup(word, &wsaData) != 0)
{
printf("套接字库加载失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
return -1;
}
if (LOBYTE(wsaData.wVersion != 2) || HIBYTE(wsaData.wHighVersion != 0))
{
printf("套接字库加载失败!\n");
WSACleanup();
return -1;
}
int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
if (sockSrv == INVALID_SOCKET)
{
printf("套接字创建失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
sockaddr_in addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_port = htons(6666);
if (int res = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))!= 0)
{
printf("套接字绑定失败!");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
if (int res = listen(sockSrv, 5) != 0)
{
printf("监听套接字失败!");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
sockaddr_in addrClient;
int addrlen;
while (1)
{
addrlen = sizeof(SOCKADDR);
int sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &addrlen);
if (sockClient == INVALID_SOCKET)
{
printf("套接字连接失败!");
int err = GetLastError();
printf("errNum = %d\n", err);
continue;
}
char sendBuf[200] = { 0 };
char recvBuf[200] = { 0 };
sprintf(sendBuf, "hello ,this is %s", inet_ntoa(addrSrv.sin_addr));
send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);
recv(sockClient, recvBuf, sizeof(recvBuf), 0);
printf("%s\n", recvBuf);
closesocket(sockClient);
}
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
客户端:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<winsock2.h>
using namespace std;
int main()
{
WORD word = MAKEWORD(2, 0);
WSADATA wsaData;
if (WSAStartup(word, &wsaData) != 0)
{
printf("套接字库加载失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
return -1;
}
if (LOBYTE(wsaData.wVersion != 2) || HIBYTE(wsaData.wHighVersion != 0))
{
printf("套接字库加载失败!\n");
WSACleanup();
return -1;
}
int sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (sockClient == INVALID_SOCKET)
{
printf("套接字创建失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
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(6666);
if (connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) != 0)
{
printf("套接字连接失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
char recvBuf[200] = { 0 };
char sendBuf[200] = { 0 };
int recvLen = recv(sockClient, recvBuf, sizeof(recvBuf), 0);
printf("%s\n",recvBuf);
sprintf(sendBuf," my name is zhangsan");
send(sockClient,sendBuf,strlen(sendBuf)+1,0);
closesocket(sockClient);
WSACleanup();
system("pause");
return 0;
}
UDP例子:
服务端:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<winsock2.h>
using namespace std;
int main()
{
WORD word = MAKEWORD(2, 0);
WSADATA wsaData;
if (WSAStartup(word, &wsaData) != 0)
{
printf("套接字库加载失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
return -1;
}
if (LOBYTE(wsaData.wVersion != 2) || HIBYTE(wsaData.wHighVersion != 0))
{
printf("套接字库加载失败!\n");
return -1;
WSACleanup();
}
int sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
if (sockSrv == INVALID_SOCKET)
{
printf("创建套接字失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
sockaddr_in addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_port = htons(6000);
if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) != 0)
{
printf("套接字绑定失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
sockaddr_in addrClient;
int addrlen = sizeof(SOCKADDR);
while (1)
{
char sendBuf[200] = { 0 };
char recvBuf[200] = { 0 };
int recvLen = recvfrom(sockSrv, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&addrClient, &addrlen);
if (recvLen > 0)
{
printf("%s\n", recvBuf);
}
sprintf(sendBuf, "hello ,this is %s", inet_ntoa(addrSrv.sin_addr));
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, addrlen);
}
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
客户端:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<winsock2.h>
using namespace std;
int main()
{
WORD word = MAKEWORD(2, 0);
WSADATA wsaData;
if (WSAStartup(word, &wsaData) != 0)
{
printf("套接字库加载失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
return -1;
}
if (LOBYTE(wsaData.wVersion != 2) || HIBYTE(wsaData.wHighVersion != 0))
{
printf("套接字库加载失败!\n");
return -1;
WSACleanup();
}
int sockClient = socket(AF_INET, SOCK_DGRAM, 0);
if (sockClient == INVALID_SOCKET)
{
printf("创建套接字失败!\n");
int err = GetLastError();
printf("errNum = %d\n", err);
WSACleanup();
return -1;
}
sockaddr_in addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_port = htons(6000);
int addrlen = sizeof(SOCKADDR);
char sendBuf[200] = { 0 };
char recvBuf[200] = { 0 };
sprintf(sendBuf, "my name is demaxiya", inet_ntoa(addrSrv.sin_addr));
sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, addrlen);
int recvLen = recvfrom(sockClient, recvBuf, sizeof(recvBuf), 0,(SOCKADDR*)&addrSrv, &addrlen);
if (recvLen > 0)
{
printf("%s\n", recvBuf);
}
closesocket(sockClient);
WSACleanup();
system("pause");
return 0;
}
错误不知道有没有,有也别慌。