这是多线程服务端
#include
#include
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)
// 客户端结构体
typedef struct ThreadNode
{
int index;
pthread_t* Thread;
SOCKET Client;
struct ThreadNode * next;
ThreadNode()
{
index = 0;
this->next = NULL;
}
} hThread;
// 增加客户端结构体
hThread *clientHeadNote, *clientEndNote;
hThread * addClient()
{
hThread * ClientNote = new hThread();
return ClientNote;
}
// 每个建立链接的客户端分配一个函数,每个线程运行一个这样的函数
void* ThreadClient(void*)
{
if (clientEndNote == NULL)
{
printf("empty Link\n");
return 0;
}
char revData[255];
SOCKET sClient = clientEndNote->Client;
printf("client logged in \n");
while (1)
{
int ret = recv(sClient, revData, 255, 0); // 接收数据
if (ret > 0)
{
revData[ret] = 0x00;
printf(revData);
printf("\n");
if (strcmp("log_out", revData) == 0) // 新功能:加入客户端控制签退
{
char a[255];
strcpy(a, "已注销\n");
send(sClient, a, 255, 0); // 反馈:向客户端sClienr发送“已注销”消息,发送长度为255个字节,1是指不阻塞。
closesocket(sClient);
printf("签退成功\n");
}
else
{
char a[255];
strcpy(a, "服务器已收到消息\n");
send(sClient, a, 255, 0);
}
}
}
closesocket(sClient);
}
int main(int argc, char* argv[])
{
// 初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}
// 创建套接字:AF_INET:TCP/IP 协议,数据流,TCP 传输
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888); // 端口和客户端端口要相同,否则链接建立不上
sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 这个是any指的是不限制访问IP
// char loa[16] = "192.168.15.189"; // 这是指定 IP发送数据
// sin.sin_addr.S_un.S_addr = inet_addr(loa); // 加入指定 IP
// 初始化slisten,加载上面代码设置的属性。
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) // 把sin强制类型转换为sockadd,sin原来是sockaddr_in类型
{
printf("bind error !");
}
//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}
// 以上可以认为是建立服务器联系的固定格式
//这里是多线程服务器,接受多个客户端,可以对比单线程看看多了什么,哪些函数的位置调整了
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
while (true)
{
printf("等待连接...\n");
SOCKET sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
}
else
{
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
if (clientHeadNote == NULL)
{
clientHeadNote = addClient();
clientEndNote = clientHeadNote;
}
// 必须要先创建给end节点,然后再创建线程,因为线程的参数通过全局变量传入,而不是通过线程生成函数传入
// 原来线程参数除了 pthread_create()还能通过全局变量传参数
hThread* clientnode = (hThread*)malloc(sizeof(hThread)); // 创建客户端结构体
clientnode->Client = sClient;
clientnode->next = NULL;
clientHeadNote->next = clientnode;
clientEndNote = clientnode;
clientnode->Thread = (pthread_t*)malloc(sizeof(pthread_t)); // 创建线程变量
clientnode->index = pthread_create(clientnode->Thread, NULL, ThreadClient, NULL); // 创建线程并执行
}
}
printf("server is closing\n");
hThread * p;
while (clientHeadNote) //释放占用的内存空间
{
p = clientHeadNote;
clientHeadNote = clientHeadNote->next;
delete p;
}
closesocket(slisten);
WSACleanup();
return 0;
}
这是单线程服务端
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#pragma waring(disable :4996)
int main()
{
WORD sockVersion = MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(sockVersion,&wsaData)!=0)
{
printf("版本与结构体不匹配\n");
return 0;
}
SOCKET slisten = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(slisten==INVALID_SOCKET)
{
printf("socket error\n");
return 0;
}
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if(bind(slisten,(LPSOCKADDR)&sin,sizeof(sin))==SOCKET_ERROR)
{
printf("bind error\n");
}
if(listen(slisten,5)==SOCKET_ERROR)
{
printf("listen error\n");
return 0;
}
sockaddr_in remoteAddr;
int nAddrlen=sizeof(remoteAddr);
char rev[255];
char sen[244];
strcpy(sen,"okk\n");
printf("等待连接\n");
SOCKET sClient = accept(slisten,(SOCKADDR*)&remoteAddr,&nAddrlen);
if(sClient==INVALID_SOCKET)
{
printf("accpet error\n");
}
int ret=0;
while(true)
{
ret=0;
ret =recv(sClient,rev,255,0);
if(ret>0)
{
printf("recieve\n");
printf("ret = %d\n",ret);
printf("消息为:%s\n",rev);
send(sClient,sen,255,0); // 0 代表阻塞发送,缓存起来,按顺序一个一个发
strcpy(rev,""); // 清空旧数据
}
Sleep(100);
}
}
这是客户端
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)
//格式和服务端一样
int main(int argc, char* argv[])
{
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if (WSAStartup(sockVersion, &data) != 0)
{
return 0;
}
SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sclient == INVALID_SOCKET)
{
printf("无效的 socket !");
return 0;
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET; // 表示使用IPv4地址协议 https://blog.csdn.net/u012736362/article/details/130392547
serAddr.sin_port = htons(8888);
char loa[16] = "127.0.0.1"; // 对方IP地址。当服务器的电脑的IP , win建+r 输入cmd,打开命令行,输入再直接 ipconfig/all可查看IP。
serAddr.sin_addr.S_un.S_addr = inet_addr(loa);// IP是读取目标机器的IP,由于一台电脑当服务器和客户端,127.0.0.1是自回路,自己发送给自己
//链接发消息
while(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
//主动连接服务器
printf("连接错误 !\n");
Sleep(1000);
}
puts("连接成功!!");
puts("你现在可以向服务器发送信息:");
char sendData[100];
char recData[255];
while (1)
{
scanf("%s",sendData);
send(sclient, sendData, strlen(sendData), 0); // 发送消息 0 代表阻塞 1 代表不阻塞
int ret = recv(sclient, recData, 255, 0); // 接收消息 0 代表不执行这句,下面的都不执行
if (ret > 0)
{
printf(recData);
}
}
closesocket(sclient);
WSACleanup();
return 0;
}