【实现方案一】
客户端:
//附加依赖项:Ws2_32.lib
//库文件: Winsock2.h
// Windows 头文件:
#include
#include< Winsock2.h>
#include
int main()
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //initiates use of WS2_32.DLL by a process.
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
SOCKET ClientSocket;
ClientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (ClientSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld, ClientSocket: %d \n", WSAGetLastError(), ClientSocket);
WSACleanup();
getchar();
return -1;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
int fromlen = -1;
int count = 0;
char recvBuf[100];
char requestStr[100];
SYSTEMTIME curTime;
while(1){
GetSystemTime(&curTime);
//返回当前系统时间(毫秒)
sprintf(requestStr, "%d", curTime.wSecond);
memset(recvBuf, 0, sizeof(recvBuf));
sendto(ClientSocket, requestStr, strlen(requestStr)+1, 0, (struct sockaddr *)&service, sizeof(struct sockaddr));
recv(ClientSocket, recvBuf, 100, 0);
//如果recv不到,就会阻塞在这里,一致等待recv
printf("Client Receive: %s \n", recvBuf);
count++;
if (count > 10)
{
break;
}
}
closesocket(ClientSocket);
getchar();
return 0;
}
// Windows 头文件:
#include
#include< Winsock2.h>
int main()
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //initiates use of WS2_32.DLL by a process.
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
SOCKET ListenSocket;
ListenSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld, ListenSocket: %d \n", WSAGetLastError(), ListenSocket);
WSACleanup();
getchar();
return -1;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
if (bind( ListenSocket, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
printf("bind() failed.\n");
closesocket(ListenSocket);
getchar();
return -1;
}
int size = 0;
char buf[100];
int count = 0;
while(1){
memset(buf, 0, sizeof(buf));
size=sizeof(service);
recvfrom(ListenSocket,buf,100,0, (struct sockaddr *)&service,&size);
printf("%s:%s\n",inet_ntoa(service.sin_addr),buf);
Sleep(1000);
sendto(ListenSocket,buf,strlen(buf)+1,0,
(struct sockaddr *)&service,sizeof(service));
count++;
if (count > 20)
{
break;
}
}
printf("test OK!\n");
closesocket(ListenSocket);
getchar();
return 0;
}
【实现方案二】
#ifdef WIN32
#include "stdafx.h"
#endif
#include
#include
#include< Winsock2.h>
//Ws2_32.lib
#include "pthread.h"
#pragma warning(disable:4251)
using namespace std;
using std::string;
sockaddr_in service; //服务器地址
SOCKET ClientSocket; //客户端socket
void *run(void *arg)
{
int fromlen = -1;
int count = 0;
char recvBuf[100];
char requestStr[100];
SYSTEMTIME curTime;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //允许退出线程
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //设置立即取消
for(int i = 0; ; i++) //无限循环,收发消息
{
GetSystemTime(&curTime);
//返回当前系统时间(毫秒)
sprintf(requestStr, "%d", curTime.wSecond);
memset(recvBuf, 0, sizeof(recvBuf));
sendto(ClientSocket, requestStr, strlen(requestStr)+1, 0, (struct sockaddr *)&service, sizeof(struct sockaddr));
recv(ClientSocket, recvBuf, 100, 0);
//如果recv不到,就会阻塞在这里,一致等待recv
printf("Client Receive: %s \n", recvBuf);
}
return NULL;
}
int main()
{
int ret = 0;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //initiates use of WS2_32.DLL by a process.
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
ClientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (ClientSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld, ClientSocket: %d \n", WSAGetLastError(), ClientSocket);
WSACleanup();
getchar();
return -1;
}
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
pthread_attr_t attr; //线程属性
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t recvMsgThrd ;
ret = pthread_create(&recvMsgThrd , &attr, run, NULL); //创建线程
if (ret){
printf("create thread failed!\n");
return -1;
}
char recvBuf[100];
for(int i = 0; i < 10; i++){
//UDP的socket,如果不绑定的话,系统会分配一个本地的socket端口,用以向对端发送消息
//如果main和pid线程使用同一个socket向service发送消息,接收消息时,轮到哪个线程接收,就由哪个线程接收
//因此,有可能出现main接收了client的消息
sendto(ClientSocket, "hello from main", 15, 0, (struct sockaddr *)&service, sizeof(struct sockaddr));
recv(ClientSocket, recvBuf, 100, 0);
//如果recv不到,就会阻塞在这里,一致等待recv
printf("Main Receive: %s \n", recvBuf);
}
if( !pthread_cancel(pid) ) //向线程发送CANCLE消息
{
printf( "pthread_cancel OK\n " );
}
closesocket(ClientSocket); //关闭socket
getchar();
return 0;
}
【实现方案三】
在方案二中,线程退出方式应该尽量避免,这样的异常退出,不利于线程资源的回收。另外,socket是阻塞方式的,下面的例子中以select方式实现同样的功能:
//服务端
#include
#include< Winsock2.h>
int main()
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //initiates use of WS2_32.DLL by a process.
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
SOCKET ListenSocket;
ListenSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld, ListenSocket: %d \n", WSAGetLastError(), ListenSocket);
WSACleanup();
getchar();
return -1;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(20719);
if (bind( ListenSocket, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
printf("bind() failed.\n");
closesocket(ListenSocket);
getchar();
return -1;
}
fd_set readFds;
//可读socket集
FD_ZERO(&readFds);
FD_SET(ListenSocket, &readFds);
int size = 0;
char buf[100];
int count = 0;
int ret = 0;
while(1){
ret = select(ret + 1, &readFds, NULL, NULL, NULL); //当select返回>0时,ListenSocket可读,这样可避免阻塞在recv函数
if(ret > 0)
{
if(FD_ISSET(ListenSocket, &readFds))
{
memset(buf, 0, sizeof(buf));
size=sizeof(service);
recvfrom(ListenSocket,buf,100,0, (struct sockaddr *)&service,&size);
printf("%s:%d:%s\n",inet_ntoa(service.sin_addr),service.sin_port, buf);
Sleep(5000); //接收完毕后,睡眠5秒,再发送
sendto(ListenSocket,buf,strlen(buf)+1,0,
(struct sockaddr *)&service,sizeof(service));
}
}
Sleep(1);
}
closesocket(ListenSocket);
getchar();
return 0;
}
//客户端
#ifdef WIN32
#include "stdafx.h"
#endif
#include "testClient.h"
#include "pthread.h"
using namespace Json;
pthread_t recvMsgThrd;
sockaddr_in service;
SOCKET ClientSocket;
int isConnect = 0;
void *run(void *arg)
{
int fromlen = -1;
int count = 0;
char recvBuf[100];
char requestStr[100];
SYSTEMTIME curTime;
int ret = 0;
fd_set readFds;
//可读socket集
struct timeval tv;
//select超时时间
for(int i = 0; ; i++)
{
if (isConnect == 0) //子线程检测isConnect 值,当为0时,退出
{
printf("connect closed !\n");
break;
}
GetSystemTime(&curTime);
//返回当前系统时间(毫秒)
sprintf(requestStr, "%d", curTime.wSecond);
sendto(ClientSocket, requestStr, strlen(requestStr)+1, 0, (struct sockaddr *)&service, sizeof(struct sockaddr)); //向服务端发送本地时间(秒)
FD_ZERO(&readFds);
FD_SET(ClientSocket, &readFds);
tv.tv_sec = 3; //秒
tv.tv_usec =500; //毫秒
ret = select(ret + 1, &readFds, NULL, NULL, &tv);
printf("----select(%d) ret : %d \n", i, ret);
if(ret > 0)
{
if(FD_ISSET(ClientSocket, &readFds))
{
memset(recvBuf, 0, sizeof(recvBuf));
ret = recv(ClientSocket, recvBuf, 100, 0);
//如果recv不到,就会阻塞在这里,一致等待recv
printf("Client Receive: %s \n", recvBuf);
}
}
}
printf("thread return !\n");
return NULL;
}
int main()
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //initiates use of WS2_32.DLL by a process.
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
ClientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (ClientSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld, ClientSocket: %d \n", WSAGetLastError(), ClientSocket);
WSACleanup();
getchar();
return -1;
}
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service .sin_port = htons(20719);
int fromlen = -1;
int count = 0;
char recvBuf[100];
char requestStr[100];
SYSTEMTIME curTime;
int ret = 0;
pthread_t pid;
isConnect = 1;
ret = pthread_create(&pid, NULL, run, NULL);
if (ret){
printf("create thread failed!\n");
return -1;
}
Sleep(15000); //主线程在运行大概15秒之后,置isConnect为0
isConnect = 0;
void *status = NULL;
pthread_join(recvMsgThrd, &status); //等待recvMsgThrd线程退出
closesocket(ClientSocket); //关闭socket
ClientSocket = -1;
printf("close socket !\n");
getchar();
return 0;
}
运行结果:
server:
client:
结论:
Server:Server每隔5秒钟向Client发送一次,但是,Client向Server每隔3.5秒发送一次。因此,可见系统缓存了阻塞的数据包。
当Client的发送线程退出后,仍然可以从socket的缓冲中读出剩余几次的数据。
Client:Client子线程每隔3.5秒向server发送一次,子线程运行时间大约15秒;