跨平臺網絡編程

myudp - 源碼文件 - 點擊下載

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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章