C实现socket 服务端和客户端
一. 服务端和客户端服务流程图
二. 服务端的实现
1.1 服务端和客户端公用文件tcpSocket.h, tcpSocket.cpp
#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H
#include<WinSock2.h> // windows平台网络库头文件
#include<WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib") // 库文件
#include<stdbool.h>
#include<stdio.h>
// 服务器端口
#define PORT 8899 // [0, 65536)
// 封装错误提示
#define err(errMsg) printf("[line:%d] %s failed code %d\n", __LINE__, errMsg, WSAGetLastError());
// 初始化socket
bool init_Socket();
// 关闭socket
bool close_Socket();
// 发送消息
bool sendMsg(int fd, const char* msg);
// 接收消息
bool recvMsg(int fd, char* buf, int bufSize);
#endif // !_TCPSOCKET_H_
#include "tcpSocket.h"
bool init_Socket()
{
WSADATA wsadata;
if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata)) {
err("WSAStartup");
return false;
}
return true;
}
bool close_Socket()
{
if (0 != WSACleanup()) {
err("WSACleanup");
return false;
}
return true;
}
// 发送消息
bool sendMsg(int fd, const char* msg) {
int ret = send(fd, msg, strlen(msg) + 1, 0);
if (ret == -1) {
err("send msg");
return false;
}
return true;
}
// 接收消息
bool recvMsg(int fd, char* buf, int bufSize) {
//printf("buf:%d\n", sizeof(buf)); // 这里的大小为指针的大小
int len = recv(fd, buf, bufSize, 0);
if (len > 0) {
printf("服务端说: %s\n", buf);
return true;
}
else if (len == 0)
{
printf("服务器端已经断开了...\n");
}
else {
perror("recv");
}
return false;
}
1.2 服务端主程序 server.cpp
#include "../tcpSocket/tcpSocket.h"
#include <thread>
// 客户端信息结构体
struct SockInfo {
sockaddr_in addr;
int fd;
};
// 客户端工作函数
void* working(SockInfo *pinfo);
// 客户端数组
SockInfo infos[512] = { 0 };
int main() {
// 初始化网络库
init_Socket();
// 1. 创建空 socket
// param1: 地址协议族 ipv4 ipv6
// param2: 传输协议类型 流式套接字 数据报
// param3: 使用具体的某个传输协议
SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
{
err("socket");
return -1;
}
// 2. 给socket绑定ip地址和端口号
sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.S_un.S_addr = INADDR_ANY;
int ret = bind(fd, (sockaddr*)&saddr, sizeof(saddr));
if (ret == -1) {
err("bind");
return -1;
}
//3. 监听
ret = listen(fd, 10);
if (ret == -1)
{
err("listen");
return -1;
}
printf("等待客户端连接...\n");
// 初始化存客户端的数组
int max = sizeof(infos) / sizeof(infos[0]);
for (int i = 0; i < max; ++i)
{
infos[i].fd = -1;
}
//4. 如果有客户端请求连接
int addrLen = sizeof(sockaddr_in);
while (1) {
// 为新客户端连接准备存储位置
SockInfo* pinfo = nullptr;
for (int i = 0; i < max; ++i)
{
if (infos[i].fd == -1) {
pinfo = &infos[i];
break;
}
}
// 挂起,等待新连接,有新连接则运行
int cfd = accept(fd, (struct sockaddr*)&pinfo->addr, &addrLen);
if (cfd == -1) {
err("accept");
break;
}
pinfo->fd = cfd;
// 有客户端连接,启动工作线程
std::thread Worker(working, pinfo);
Worker.detach();
}
closesocket(fd);
close_Socket();
return 0;
}
/*
* 打印客户端信息,接收和发送客户端消息
*/
void* working(SockInfo *pinfo)
{
//SockInfo* pinfo = (SockInfo*)arg;
// 连接成功,打印客户端的IP和端口
char ip[32];
printf("客户端fd: %d, IP:%s, 端口:%d\n",
pinfo->fd,
inet_ntop(AF_INET, &pinfo->addr.sin_addr.S_un, ip, sizeof(ip)),
ntohs(pinfo->addr.sin_port)
);
//5. 可以和客户端进行通信
// 从指定的socket接受消息
while (1) {
char buf[1024];
// 接收消息
if (recvMsg(pinfo->fd, buf, sizeof(buf))) {
sendMsg(pinfo->fd, buf);
}
}
int ret = closesocket(pinfo->fd);
if (ret == 0)
{
printf("释放客户端fd:%d\n", pinfo->fd);
}
pinfo->fd = -1;
return NULL;
}
三 客户端的实现
3.1 客户端主程序 client.cpp
#include <iostream>
#include "../tcpSocket/tcpSocket.h"
int main()
{
init_Socket();
// 1. 创建空 socket
// param1: 地址协议族 ipv4 ipv6
// param2: 传输协议类型 流式套接字 数据报
// param3: 使用具体的某个传输协议
SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
{
err("socket");
return -1;
}
// 2. 与服务器建立连接
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.S_un.S_addr);
int ret = connect(fd, (sockaddr*)&addr, sizeof(addr));
if ( ret == -1)
{
err("connect");
return -1;
}
int number = 0;
while (1) {
// 给服务器发送消息
sendMsg(fd, "你好,我是客户端");
// 接收服务器消息
char recvBuf[BUFSIZ];
memset(recvBuf, 0, sizeof(recvBuf));
if (!recvMsg(fd, recvBuf, sizeof(recvBuf))){
break;
}
printf("接收到的消息:%s\n", recvBuf);
Sleep(1000);
}
closesocket(fd);
close_Socket();
return 0;
}