这是多线程服务端
#include <stdio.h>
#include <winsock2.h>
#include <pthread.h>
#include <string.h>
#include <conio.h>
#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[])
{// 初始化WSAWORD 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 <stdio.h>
#include <winsock2.h>
#include <conio.h>
#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 <winsock2.h>
#include <stdio.h>
#include <pthread.h>
#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/130392547serAddr.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;
}