《TCP/IP网络编程》笔记6-基于UDP的服务器端客户端

  • 理解UDP
  • UDP服务端与客户端的实现

理解UDP

UDP传输就像发信件,发快递,无法确定对方收到,数据也可能丢。UDP即使对方不在线,也可以“正常”发送数据,即对于发送者来说是不care接收端是否在线的,如果对方不在线,那么数据就全丢了。
但是,UDP也有优点,比如:
1. 编程简单
2. UDP不进行流控制,不需要发送ACK应答,也不需要发送SEQ分配给数据包序列号,性能会高出TCP很多

所以在重视性能而不是可靠性的场合,例如实时性要求较高的视频音频传输,即便丢包也最多是画面抖动或杂音,速度才是最重要的,因此首选UDP。

UDP服务端与客户端的实现

UDP传输,无需在连接状态下进行,也就是说UDP编程中无需acceptlisten这样的函数。
另外,只需创建一个UDP套接字就可以与多台主机通讯,即UDP**不面对连接**,不会保存之前的目标ip,也因此,每次发送数据都要输入目标IP。
TCP的发送端,在调用connect函数时,会自动分配IP和端口,但是UDP不需要connect,因此是在send函数调用时自动分配的。

bind函数不区分UDP和TCP,因此也可以通过bind函数强制指定。

从以下代码也可以看出,对于UDP传输,服务端实现和客户端实现是及其类似的。
客户端代码:

// tcpserverwin.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdafx.h"
#include 
#include 
#include 
#pragma comment (lib,"ws2_32.lib") 

#define BUF_SZ 30
int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET svrSocket;
    SOCKADDR_IN servAddr,clientAddr;
    int strlen;
    char message[BUF_SZ];
    int szClientAddr;
    if(argc!=3){
        printf("usage : %s ip port\n",argv[0],argv[1]);
        return 0;
    }
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
        printf("WSAStartup  failed\n");
        return 0;
    }
    svrSocket = socket(PF_INET,SOCK_DGRAM,0);
    if(svrSocket == INVALID_SOCKET){
        printf("socket error\n");
        return 0;
    }
    memset(&servAddr,0,sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(atoi(argv[1]));

    //int bind(hSocket,(SOCKADDR*)&servAddr,sizeof(servAddr));


    szClientAddr = sizeof(clientAddr);

    for (;;)
    {
        fputs("insert message , q to quit\n",stdout);
        fgets(message,BUF_SZ,stdin);
        if(!strcmp(message,"q\n")){
            break;
        }

        // 如果目标地址是固定的,这里可以不每次都指定目标地址,
        // 在循环外使用connect函数绑定一下目标ip和端口号即可
        memset(&clientAddr,0,sizeof(clientAddr));
        clientAddr.sin_family = AF_INET;
        clientAddr.sin_addr.s_addr = inet_addr(argv[1]);
        clientAddr.sin_port = htons(atoi(argv[2]));

        sendto(svrSocket,message,sizeof(message),0,
            (SOCKADDR*)&clientAddr,sizeof(clientAddr));

        strlen=recvfrom(svrSocket,message,BUF_SZ,0,
            (SOCKADDR*)&clientAddr,&szClientAddr);
        message[strlen] = 0;
        printf("recv: %s\n",message);

    }

    closesocket(svrSocket);
    WSACleanup();
    return 0;
}

服务端代码:

// tcpserverwin.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdafx.h"
#include 
#include 
#include 
#pragma comment (lib,"ws2_32.lib") 

#define BUF_SZ 30
int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET svrSocket;
    SOCKADDR_IN servAddr,clientAddr;
    int strlen;
    char message[BUF_SZ];
    int szClientAddr;
    if(argc!=2){
        printf("usage : %s port\n",argv[0]);
        return 0;
    }
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
        printf("WSAStartup  failed\n");
        return 0;
    }
    svrSocket = socket(PF_INET,SOCK_DGRAM,0);
    if(svrSocket == INVALID_SOCKET){
        printf("socket error\n");
        return 0;
    }
    memset(&servAddr,0,sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(atoi(argv[1]));

    //int bind(hSocket,(SOCKADDR*)&servAddr,sizeof(servAddr));
    if(bind(svrSocket,(SOCKADDR*)&servAddr,sizeof(servAddr)) == SOCKET_ERROR){
        printf("bind port [%d] error\n",atoi(argv[1]));
        return 0;
    }

    szClientAddr = sizeof(clientAddr);

    for (;;)
    {
        strlen=recvfrom(svrSocket,message,BUF_SZ,0,
            (SOCKADDR*)&clientAddr,&szClientAddr);
            sendto(svrSocket,message,strlen,0,
                (SOCKADDR*)&clientAddr,sizeof(clientAddr));
    }

    closesocket(svrSocket);
    WSACleanup();
    return 0;
}

你可能感兴趣的:(c,网络编程)