udp.h
//C++为了实现函数的重载,在编译的时候要将函数名进行重命名
//C没有函数重载,所以不会在编译的时候带对函数名进行重命名
//所以在C++中如果要使用C的函数,需要明确的告诉编译器,这是一个C的函数,不要对其进行重命名
//使用exten "C"关键字来实现
//extern "C"
//{
//int socket_send(const char *IP);
//int socket_recv();
//}
int socket_send(const char *IP);
int socket_recv();
udp.cpp
#include <string.h>
#include <stdio.h>
#ifdef MYLINUX
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SOCKET int
#else
#include <winsock2.h>
#endif
//使用socket发消息
int socket_send(const char *IP)
{
#ifndef MYLINUX
//初始化socket
DWORD ver;
WSADATA wsaData;
//在调用WSAStatrtup要告诉windows我们要用什么版本的socket
ver = MAKEWORD(1,1);
//windows要求,只要用socket,第一步就要调用这个函数
WSAStartup(ver,&wsaData);
//-----初始化完成-----//
#endif
//建立一个socket
//第一个参数是指定socket要用那个协议,AF_INET代表要用TCP/IP协议
//第二个参数SOCK_DGRAM意思是要用UDP协议
//第三个参数一般默认填0
//SOCKET就是一个指向无符号整数的指针
SOCKET st = socket(AF_INET,SOCK_DGRAM,0);
/*
struct sockaddr_in {
short sin_family; //代表我要指向一个什么协议的地址
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sin_zero[8]; //这是为了兼容老结构sockaddr的字节长度,在sendto()函数中使用的还是老结构sockaddr
};
//*/
struct sockaddr_in addr;
//初始化结构体
memset(&addr,0,sizeof(addr));
//代表要使用一个TCP/IP的地址
addr.sin_family = AF_INET;
//htons = host to net short
//htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。
addr.sin_port = htons(8080);
//s_addr = unsigned long
//inet_addr()可以将一个字符串的ip地址转化成一个整数
//首先给自己发消息,如果发生错误,可以检查是网络问题还是自己代码的问题
//科普:在一个ip地址中,0 代表自己,255 代表广播
addr.sin_addr.s_addr = inet_addr(IP);
//inet_addr转换原理
//unsigned long laddr = inet_addr("192.168.6.200");
//unsigned char *p = &laddr;
//printf("%u,%u,%u,%u\n",*(p),*(p+1),*(p+2),*(p+3));
char buf[1024] = {0};
size_t rc;
while(1){
memset(buf,0,sizeof(buf));
scanf("%s",&buf);
if(buf[0] == '0'){
break;
}
//发送udp的数据
//网络编程原理:尽可能用小的数据包实现大的功能
//这里如果使用sizeof() = 1024,会浪费带宽,所以使用strlen
//第一个参数是socket
//第二个参数是数据包
//第三个参数是将要发送的长度
//第四个参数是优先级
//第五个参数是IP地址结构体的地址
//第六个参数是IP地址结构体的长度
//函数返回值size_t代表发送了多少内容出去
//说明:在sendto中第五个参数是IP地址结构体的地址,使用的还是老的IP地址结构体sockaddr,但是我们现在为了方便,都是使用新的IP地址结构体sockaddr_in,所以在使用sendto时还需要将新的IP地址结构体sockaddr_in强制转化为老的IP地址结构体sockaddr
rc = sendto(st,buf,strlen(buf),0,(struct sockaddr *)&addr,sizeof(addr));
}
#ifdef MYLINUX
close(st);//使用完socket要将其关闭
#else
//使用完成后需要将socket进行关闭
closesocket(st);
//释放win socket内部的相关资源
WSACleanup();
#endif
return rc;
}
int socket_recv()
{
#ifndef MYLINUX
DWORD ver;
WSADATA wsaData;
ver = MAKEWORD(1,1);
WSAStartup(ver,&wsaData);
#endif
SOCKET st = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
//作为接受方,不要指定具体的ip地址,接收的ip就是程序运行主机的ip,在多网卡的情况下才可能需要写
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int rc = 0;
//bind()将端口号和程序进行绑定,对于操作系统来讲,需要避免两个程序绑定同一个端口号
//第一个参数是socket
//第二个参数是IP地址结构体的地址
//第三个参数是IP地址结构体的长度
//如果bind()返回 -1,这说明这个端口已经被人使用了,绑定失败
if(bind(st,(struct sockaddr *)&addr,sizeof(addr)) != -1){
char buf[1024] = {0};
struct sockaddr_in sendaddr;
memset(&sendaddr,0,sizeof(sendaddr));
#ifdef MYLINUX
socklen_t len;
#else
int len;
#endif
len = sizeof(sendaddr);
while(1){
memset(buf,0,sizeof(buf));
//接收upd的数据
//阻塞,一个函数在没有返回之前,程序被挂起
//recvfrom 就是一个阻塞的函数
//recvfrom 阻塞原理
//|--------------------------------------------------|
//|socket,recvfrom - 从底层接收的buffer读取数据,当接收的buffer没有数据时,recvfrom就在死等,这就是阻塞
//|--------------------------------------------------|
//|UDP,TCP
//|--------------------------------------------------|
//|IP,缓存区buffer,如果有数据进来,将会被存储在这个区域
//|--------------------------------------------------|
//|底层硬件
//|--------------------------------------------------|
rc = recvfrom(st,buf,sizeof(buf),0,(struct sockaddr *)&sendaddr,&len);
printf("%s-->%s\n",inet_ntoa(sendaddr.sin_addr),buf);
}
}
#ifdef MYLINUX
close(st);//使用完socket要将其关闭
#else
//不管端口绑定是成功还是失败,最后都要将资源进行释放
closesocket(st);
WSACleanup();
#endif
return rc;
}
main.cpp
#include "udp.h"
#ifndef MYLINUX
#include <iostream>
#endif
//#include "basic.h"
using namespace std;
int main(int argc,char *args[])
{
if(argc > 1){
socket_send(args[1]);
}
else{
socket_recv();
}
return 0;
}
window下需要myudp.pro文件
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
#-lWs2_32,link Ws2_32.lib
LIBS += -lWs2_32
SOURCES += \
main.cpp \
udp.cpp \
basic.cpp
HEADERS += \
udp.h \
basic.h
linux下需要makefile文件
CC=g++
CFLAGS=-DMYLINUX
SRCS=main.cpp\
basic.cpp\
udp.cpp
OBJS=$(SRCS:.cpp=.o)
EXEC=myapp
start: $(OBJS)
$(CC) -o $(EXEC) $(OBJS)
.cpp.o:
$(CC) -o $@ -c $< $(CFLAGS)
clean:
rm -rf $(OBJS)