目录
说明
这个示例是写了一个服务端和一个客户端,客户端发数据,服务器接数据。
服务端地址:10.131.8.185 客户端地址:10.131.8.164
注意事项:
1、需要先运行服务端,再运行客户端
2、服务器和客户端都在同一台机器运行的时候,ip地址双方可以都设置为127.0.0.1
3、服务器和客户端不在同一台机器运行的时候,服务器绑定套接字的时候,ip需要设置为自己的ip,客户端连接套接字的ip需要设置为连接方的ip(也就是服务器ip)
4、如果在vs中Ctrl+F5运行控制台不显示数据,可以考虑直接运行编译生成的exe文件
效果展示
客户端和服务端不同机器上
服务端代码
#include <iostream>
#include <winsock.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#define DEF_PORT 6666
//服务端
int main(int argc, char *argv[])
{
//初始化DLL
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
cout << "初始化DLL失败" << endl;
goto end;
}
cout << "初始化DLL成功" << endl;
//创建套接字
SOCKET serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
SOCKADDR_IN serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET; //使用ipv4地址
serverAddr.sin_addr.S_un.S_addr = inet_addr("10.131.8.185");//本机地址,如果客户端和服务器在同一机器,可以使用127.0.0.1
serverAddr.sin_port = htons(DEF_PORT); //端口号
if (bind(serverSock, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
cout << "套接字绑定失败" << endl;
goto end;
}
cout << "套接字绑定成功" << endl;
//进入监听状态
if (listen(serverSock, SOMAXCONN) < 0)//参数2:挂机连接队列的最大长度,SOMAXCONN:让系统自动选择合适的个数
{
cout << "套接字监听失败" << endl;
goto end;
}
cout << "套接字监听成功" << endl;
//接收客户端请求
SOCKADDR_IN clientAddr;
int len = sizeof(sockaddr_in);
SOCKET clientSock = accept(serverSock, (SOCKADDR*)&clientAddr, &len);//阻塞等待客户端连接
if (clientSock == SOCKET_ERROR)
{
cout << "与客户端连接失败" << endl;
goto end;
}
cout << "与客户端连接成功..." << endl;
//接收数据
int recvLen = 0;
char recvBuf[1024];
memset(recvBuf, 0, 1024);
while (1)
{
recvLen = recv(clientSock, recvBuf, 1024, NULL);//接收消息
cout << "from client " << inet_ntoa(clientAddr.sin_addr) << ": " << recvBuf << endl;
memset(recvBuf, 0, 1024);
}
end:
closesocket(serverSock);
closesocket(clientSock);
WSACleanup();//释放DLL资源
return 0;
}
客户端代码
#include <iostream>
#include <winsock.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#define DEF_PORT 6666
//客户端
int main(int argc, char* argv[])
{
//初始化DLL
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
cout << "初始化DLL失败" << endl;
goto end;
}
cout << "初始化DLL成功" << endl;
//创建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发送请求
SOCKADDR_IN sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET; //使用ipv4地址
sockAddr.sin_addr.S_un.S_addr = inet_addr("10.131.8.185");//服务器ip,如果客户端和服务端在同一机器运行可以使用127.0.0.1
sockAddr.sin_port = htons(DEF_PORT); //端口号
if (connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)//连接服务器
{
cout << "连接服务端失败, 错误码:" << WSAGetLastError() << endl;
goto end;
}
cout << "连接服务端成功..." << endl;
//向服务器发送数据
char sendBuf[1024];
memset(sendBuf, 0, 1024);
while (1)
{
cin >> sendBuf;
send(sock, sendBuf, strlen(sendBuf), NULL);//发送消息
memset(sendBuf, 0, 1024);
}
end:
closesocket(sock);
WSACleanup();//释放DLL资源
while (1);
return 0;
}
函数和结构体详情
SOCKADDR_IN
typedef struct sockaddr_in SOCKADDR_IN;
/*
* @sin_family: ip地址类型, 使用:AF_INET 和 AF_INET6,前者代表ipv4,后者ipv6
* @sin_port: 端口号, 使用:1024~65535
* @sin_addr: ip地址, 使用:sin_addr.Sun.S_addr = inet_addr("127.0.0.1")
* @sin_zero: 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
*/
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
/*
* @S_addr: IP地址,通常使用:inet_addr("127.0.0.1")
*/
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
SOCKADDR
typedef struct sockaddr SOCKADDR;
/*
* Structure used by kernel to store most
* addresses.
*/
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};
socket
/*
* @af: ip地址类型, 使用:AF_INET 和 AF_INET6,前者代表ipv4,后者ipv6
* @type: 数据传输方式, 使用:SOCK_STREAM 和 SOCK_DGRAM,前者流形式,后者报文
* @protocol: 传输协议, 使用:IPPROTO_TCP 和 IPPTOTO_UDP
*/
SOCKET PASCAL FAR socket (
_In_ int af,
_In_ int type,
_In_ int protocol);
bind
/*
* @s: ip地址类型, 使用:AF_INET 和 AF_INET6,前者代表ipv4,后者ipv6
* @addr: sockaddr结构体变量
* @namelen: sockaddr结构体长度
* @返回值: 返回SOCKET_ERROR,表示绑定失败
*/
int PASCAL FAR bind (
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR *addr,
_In_ int namelen);
listen
/*
* @s: 需要监听的套接字
* @backlog: 挂机连接队列的最大长度, 通常使用SOMAXCONN,让系统自动选择合适的个数
* @返回值: 如果小于0,表示监听失败
*/
int PASCAL FAR listen (
_In_ SOCKET s,
_In_ int backlog);
accept
/*
* @s: 服务器套接字
* @addr: 存储连接的客户端地址信息
* @addrlen:地址信息结构体长度
* @返回值: 连接的客户端套接字
*/
SOCKET PASCAL FAR accept (
_In_ SOCKET s,
_Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR *addr,
_Inout_opt_ int FAR *addrlen);
connect
/*
* @s: 服务器套接字
* @addr: 存储连接的客户端地址信息
* @addrlen:地址信息结构体长度
* @返回值: 返回SOCKET_ERROR表示失败
*/
int PASCAL FAR connect (
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR *name,
_In_ int namelen);
send
/*
* @s: 要发送的主机套接字(给谁发,就填谁)
* @buf: 发送的数据
* @len: 发送数据的长度
* @flags: 指定调用方式的标志集,一般设置为NULL, 参数有两个: MSG_DONTROUTE,MSG_OOB
*/
int PASCAL FAR send (
_In_ SOCKET s,
_In_reads_bytes_(len) const char FAR * buf,
_In_ int len,
_In_ int flags);
recv
/*
* @s: 要接收的主机套接字(谁给我发,就填谁)
* @buf: 接收的数据
* @len: 接收数据的长度
* @flags: 指定调用方式的标志集,一般设置为NULL, 参数有两个: MSG_DONTROUTE,MSG_OOB
*/
int PASCAL FAR recv (
_In_ SOCKET s,
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
_In_ int len,
_In_ int flags);