UDP通信实例程序

【实现方案一】
客户端:
//附加依赖项: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;
}

测试结果:
客户端:
UDP通信实例程序_第1张图片

服务端:
UDP通信实例程序_第2张图片

【实现方案二】
#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:
UDP通信实例程序_第3张图片

client:
UDP通信实例程序_第4张图片
结论:
Server:Server每隔5秒钟向Client发送一次,但是,Client向Server每隔3.5秒发送一次。因此,可见系统缓存了阻塞的数据包。
            当Client的发送线程退出后,仍然可以从socket的缓冲中读出剩余几次的数据。
Client:Client子线程每隔3.5秒向server发送一次,子线程运行时间大约15秒;
 

你可能感兴趣的:(多线程)