#include
以及包含
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "ws2_32.lib")
告诉 MSVC 编译器,在链接时包含ws2_32.lib
库。ws2_32.lib
是 Windows Sockets(Winsock2)库,提供了用于网络通信的函数和数据结构支持,例如:TCP/IP 协议和 UDP 协议。ws2_32.lib
,程序可以使用 Windows 的套接字函数,如socket、bind、listen、accept、send、recv
等。WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
listen_sock
,socket函数创建套接字失败时,返回INVALID_SOCKET
(值为(SOCKET)(~0)
)
/*SOCKET socket(
int af, //协议地址簇 IPV4/IPV6 AF_INET/AF_INET6
int type, //类型 流式协议 帧式协议 SOCK_STREAM/SOCK_DGRAM
int protocol //保护协议 0
);*/
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//如果创建失败时,打印错误值方便查询错误
if (INVALID_SOCKET == listen_sock)
{
printf("create listen socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
/*struct sockaddr_in {
ADDRESS_FAMILY sin_family; //协议地址簇
USHORT sin_port; //端口号
IN_ADDR sin_addr; //IP地址
CHAR sin_zero[8]; //保留字节
} ;*/
struct sockaddr_in local = { 0 };
local.sin_family = AF_INET;
//htons的功能:将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
//htonl()(Host to Network Long)函数用于将一个32位整数从主机字节顺序转换为网络字节顺序。
local.sin_port = htons(8080); //大小端问题 中间设备使用的是大端序(路由器)
//服务端 选项 网卡 127.0.0.1(本地环回)只接受哪个网卡的数据 一般写全0地址表示全都接收
//local.sin_addr.s_addr = htonl(INADDR_ANY); //默认IP地址
local.sin_addr.s_addr = inet_addr("0.0.0.0"); //将字符串IP地址转换成整数IP
将端口号与listen_sock
绑定bind(listen_sock, (struct sockaddr*)&local, sizeof(local))
/*int bind(
SOCKET s,
const struct sockaddr * name,
int namelen
);*/
/*struct sockaddr {
ADDRESS_FAMILY sa_family;
CHAR sa_data[14];
};*/
if (-1 == bind(listen_sock, (struct sockaddr*)&local, sizeof(local))) {
printf("bind socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
/*int listen(
SOCKET s,
int backlog
);*/
if (-1 == listen(listen_sock, 10)) {
printf("start listen socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
/*SOCKET accept(
SOCKET s, //监听socket
struct sockaddr * addr, //返回类型参数 返回的是 客户端的IP地址和端口号
int * addrlen //结构的大小
);*/
SOCKET client_socket = accept(listen_sock, NULL, NULL);
/*int recv(
SOCKET s, //客户端socket
char* buf, //接受的数据存到哪里
int len, //接受的长度
int flags //0
);*/
int ret = recv(client_socket, buffer, 1024, 0);
closesocket(client_socket);
将步骤4,5,6放入循环,即若客户端连接失败,则继续下一次连接
while (1)
{
SOCKET client_socket = accept(listen_sock, NULL, NULL);
if (INVALID_SOCKET == client_socket)
continue;
//将通讯放入循环,则客户端可多次与服务器进行通讯
while (1)
{
// 5.开始通讯(B/S)
char buffer[1024] = { 0 };
/*int recv(
SOCKET s, //客户端socket
char* buf, //接受的数据存到哪里
int len, //接受的长度
int flags //0
);*/
//设置回显服务器,将服务器收到的消息再次返回,同时打印收到的消息
int ret = recv(client_socket, buffer, 1024, 0);
if (ret <= 0)break;
printf("%llu : %s\n", client_socket, buffer);
send(client_socket, buffer, (int)strlen(buffer), 0);
}
// 6.关闭连接
closesocket(client_socket);
}
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == client_socket)
{
printf("create socket failed ! ! !\n");
return -1;
}
struct sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(8080);
target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target))) {
printf("connect server failed ! ! !\n");
closesocket(client_socket);
return -1;
}
while (1) {
char sbuffer[1024] = { 0 };
printf("please enter: ");
scanf("%s", sbuffer);
send(client_socket, sbuffer, (int)strlen(sbuffer), 0);
char rbuffer[1024] = { 0 };
int ret = recv(client_socket, rbuffer, 1024, 0);
if (ret <= 0)break;
printf("%s\n", rbuffer);
}
closesocket(client_socket);
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//1.创建socket套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == client_socket)
{
printf("create socket failed ! ! !\n");
return -1;
}
//2.连接服务器
struct sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(8080);
target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target))) {
printf("connect server failed ! ! !\n");
closesocket(client_socket);
return -1;
}
//3.开始通讯 send recv 回显服务器
while (1) {
char sbuffer[1024] = { 0 };
printf("please enter: ");
scanf("%s", sbuffer);
send(client_socket, sbuffer, (int)strlen(sbuffer), 0);
char rbuffer[1024] = { 0 };
int ret = recv(client_socket, rbuffer, 1024, 0);
if (ret <= 0)break;
printf("%s\n", rbuffer);
}
//4.关闭连接
closesocket(client_socket);
return 0;
}
上述方式仅能实现单客户端对服务器进行访问,实现客户端对服务器进行访问时,常采用IO多路复用的方式,但目前学习阶段,本人能力有限,只能使用多线程方法。
建立一个函数指针,将client_socket
传入
DWORD WINAPI thread_func(LPVOID lpThreadParameter)
{
SOCKET client_socket = *(SOCKET*)lpThreadParameter;
free(lpThreadParameter);
while (1)
{
// 5.开始通讯(B/S)
char buffer[1024] = { 0 };
/*int recv(
SOCKET s, //客户端socket
char* buf, //接受的数据存到哪里
int len, //接受的长度
int flags //0
);*/
int ret = recv(client_socket, buffer, 1024, 0);
if (ret <= 0)break;
printf("%llu : %s\n", client_socket, buffer);
send(client_socket, buffer, (int)strlen(buffer), 0);
}
printf("socket: %llu, disconnect.\n", client_socket);
// 6.关闭连接
closesocket(client_socket);
return 0;
}
再创建线程,使用函数指针传输参数时,先使用malloc
重新创建监听socket,避免进程运行时,client_socket
还未被调用就被释放掉的情况。
SOCKET* sockfd = (SOCKET*)malloc(sizeof(SOCKET));
*sockfd = client_socket;
//创建线程
CreateThread(NULL, 0, thread_func, sockfd, 0, NULL);
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
#include
#pragma comment(lib, "ws2_32.lib")
DWORD WINAPI thread_func(LPVOID lpThreadParameter)
{
SOCKET client_socket = *(SOCKET*)lpThreadParameter;
free(lpThreadParameter);
while (1)
{
// 5.开始通讯(B/S)
char buffer[1024] = { 0 };
/*int recv(
SOCKET s, //客户端socket
char* buf, //接受的数据存到哪里
int len, //接受的长度
int flags //0
);*/
int ret = recv(client_socket, buffer, 1024, 0);
if (ret <= 0)break;
printf("%llu : %s\n", client_socket, buffer);
send(client_socket, buffer, (int)strlen(buffer), 0);
}
printf("socket: %llu, disconnect.\n", client_socket);
// 6.关闭连接
closesocket(client_socket);
return 0;
}
int main()
{
//windows上使用网络功能需要开始网络权限
WSADATA wasData;
WSAStartup(MAKEWORD(2, 2), &wasData);
//服务端流程
// 1.创建socket套接字
/*SOCKET socket(
int af, //协议地址簇 IPV4/IPV6 AF_INET/AF_INET6
int type, //类型 流式协议 帧式协议 SOCK_STREAM/SOCK_DGRAM
int protocol //保护协议 0
);*/
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
//printf("%d\n", (int)listen_sock);
if (INVALID_SOCKET == listen_sock)
{
printf("create listen socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
// 2.给这个socket绑定一个端口号
/*struct sockaddr_in {
ADDRESS_FAMILY sin_family; //协议地址簇
USHORT sin_port; //端口号
IN_ADDR sin_addr; //IP地址
CHAR sin_zero[8]; //保留字节
} ;*/
struct sockaddr_in local = { 0 };
local.sin_family = AF_INET;
local.sin_port = htons(8080); //大小端问题 中间设备使用的是大端序(路由器)
//服务端 选项 网卡 127.0.0.1(本地环回)只接受哪个网卡的数据 一般写全0地址表示全都接收
//local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_addr.s_addr = inet_addr("0.0.0.0"); //将字符串IP地址转换成整数IP
/*int bind(
SOCKET s,
const struct sockaddr * name,
int namelen
);*/
/*struct sockaddr {
ADDRESS_FAMILY sa_family;
CHAR sa_data[14];
};*/
if (-1 == bind(listen_sock, (struct sockaddr*)&local, sizeof(local))) {
printf("bind socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
// 3.给这个socket开启监听属性
/*int listen(
SOCKET s,
int backlog
);*/
if (-1 == listen(listen_sock, 10)) {
printf("start listen socket failed ! ! ! errcode: %d\n", GetLastError());
return -1;
}
// 4.等待客户端连接
/*SOCKET accept(
SOCKET s, //监听socket
struct sockaddr * addr, //返回类型参数 返回的是 客户端的IP地址和端口号
int * addrlen //结构的大小
);*/
while (1)
{
SOCKET client_socket = accept(listen_sock, NULL, NULL);
if (INVALID_SOCKET == client_socket)
continue;
printf("new connect. socket: %llu\n", client_socket);
SOCKET* sockfd = (SOCKET*)malloc(sizeof(SOCKET));
*sockfd = client_socket;
//创建线程
CreateThread(NULL, 0, thread_func, sockfd, 0, NULL);
//while (1)
//{
// // 5.开始通讯(B/S)
// char buffer[1024] = { 0 };
// /*int recv(
// SOCKET s, //客户端socket
// char* buf, //接受的数据存到哪里
// int len, //接受的长度
// int flags //0
// );*/
// int ret = recv(client_socket, buffer, 1024, 0);
// if (ret <= 0)break;
// printf("%llu : %s\n", client_socket, buffer);
// send(client_socket, buffer, (int)strlen(buffer), 0);
//}
6.关闭连接
//closesocket(client_socket);
}
}