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)