歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux IPC udp/ip socket 編程

Linux IPC udp/ip socket 編程

日期:2017/3/1 9:10:12   编辑:Linux編程

模型

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//服務器:    

socket()              //創建socket            
struct sockaddr_in    //准備通信地址
bind()                //綁定socket和addr 
sendto()/recvfrom     //進行通信
close()               //關閉socket

//客戶端:

socket()              //創建socket:
//准備通信地址:服務器的地址
sendto()/recv()       //進行通信:
close()               //關閉socket:

socket()

//創建網絡端點,返回socket文件描述符,失敗返回-1設errno
int socket(int domain, int type, int protocol);

domain :協議族(protocol family)(網絡通訊(IP)還是本地通訊(xxx.socket))

  • AF_INET用於實現給予ipv4網絡協議的網絡協議
    type :協議(TCP還是UDP)
  • SOCK_DGRAM //數據報套接字, 實現包括但不限於UDP協議, which is不可靠,無連接的數據報通信方
    protocol: 特殊協議, 一般給0

准備通信地址:

struct sockaddr{    //主要用於函數的形參類型, 很少定義結構體變量使用, 叫做通用的通信地址類型//$man bind
    sa_family_t     sa_family;
    char            sa_data[14];
}
struct sockaddr_in{ //准備網絡通信的通信地址   //$man in.h
    sa_family_t sin_family;     //協議族, 就是socket()的domain的AF_INET
    in_port_t       sin_port;   //端口號
    struct in_addr  sin_addr;   //IP地址,
                    //當前網段的最大ip地址是廣播地址,即,xxx.xxx.xxx.255。
                    //255.255.255.255在所有網段都是廣播地址
}
struct in_addr{ 
    in_addr_t   s_addr;     //整數類型的IP地址
}

bind()

//把通信地址和socket文件描述符綁定,用在服務器端,成功返回0,失敗返回-1設errno
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd: socket文件的fd(returned by socket())
addr: 需要強制類型轉換成socketaddr_un或soketaddr_in, 參見上
addrlen: 通信地址的大小, 使用sizeof();

sendto()

//向指定的socket和相應的地址發送消息,成功返回實際發送數據的大小,失敗返回-1設errno
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

dest_addr:目標地址(收件人信息)
addrlen: 目標地址的大小

ANote

  • send(sockfd, buf, len, flags);等價於 sendto(sockfd, buf, len, flags, NULL, 0);
  • recv()/ send()表示通過sockfd收發數據, 因為tcp下, 收發之前sockfd已經和相應的地址連接了,所以不需要指定收發誰的/給誰, 但是udp因為收發時沒有連接, 所以需要指定

recvfrom()

//從指定的socket和相應的地址接受消息,並提供來電顯示的功能,成功返回實際接收的數據大小,失敗返回-1設errno
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

src_addr: 結構體指針, 用於保存數據發送方的通信地址
addrlen: 指針類型, 用於保存發送方的地址大小

Note:

  • recv(sockfd, buf, len, flags); 等價於 recvfrom(sockfd, buf, len, flags, NULL, 0);
  • accept() and recvfrom() 後面的參數是用來提供來電顯示的
  • 一個server對應多個client
  • server可以不知道client的地址, 但是client得知道server的地址
  • send data 一定要知道對方地址
  • receive data不需要知道對方地址
  • TCP/IP的socket都是SOCK_STREAM的,全程連接,通過socket就能找到對方地址, send data的話,直接丟給socket就行
  • UDP/IP的socket是SOCK_DGRAM的,不全程連接,不能通過socket找到對方,send data的話,server中需要使用recvfrom()來知道client的地址, 所以肯定要sendto();client本來就知道server的地址, 直接sendto()
  • recvfrom()的唯一意義就是在udp-server中配合sendto()使用
  • 因為不能通過socket找到對方, 只要是udp發消息, 就得通過sendto()
server client TCP/IP send();recv() send();recv() UDP/IP recvfrom();sendto() recv();sendto()

例子-一對一的upd/ip協議的服務器模型

//udp/ip server 五步走
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(){
    //1. 創建socket
    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(-1==sockfd)
        perror("socket"),exit(-1);
    
    //2. 准備通信地址
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(8888);
    addr.sin_addr.s_addr=inet_addr("176.43.11.211");

    //3. 綁定socket和通信地址
    int res=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
    if(-1==res)
        perror("bind"),exit(-1);
    printf("bind success\n");

    //4. 進行通信
    char buf[100]={0};
    struct sockaddr_in recv_addr;       //為使用recvfrom得到client地址做准備, 最終為sendto()做准備
    socklen_t len=sizeof(recv_addr);
    res=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&recv_addr,&len);
    if(-1==res)
        perror("recvfrom"),exit(-1);

    char* ip=inet_ntoa(recv_addr.sin_addr);             //將recvfrom獲得client地址轉換成點分十進制字符串
    printf("data received from client :%s is:%d\n",ip,res); 

    res=sendto(sockfd,"I received",sizeof("I received"),0,(struct sockaddr*)&recv_addr,len);//使用recvfrom獲得的client地址
    if(-1==res)
        perror("sendto"),exit(-1);

    //5. 關閉socket
    res=close(sockfd);
    if(-1==res)
        perror("close"),exit(-1);
    printf("close success\n");
        
    return 0;
}
//udp/ip client
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>  //close()
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main(){
    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(-1==sockfd)
        perror("socket"),exit(-1);
    printf("create socket succesfully\n");
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(8888);
    addr.sin_addr.s_addr=inet_addr("176.43.11.211");    //這個是server的地址, 雖然沒有connect, which means 不能通過socket找到這個地址, 但是我們還是知道這個地址的, sendto()是可以直接用的

    int res=sendto(sockfd,"hello",sizeof("hello"),0,(struct sockaddr*)&addr,sizeof(addr));
    if(-1==res)
        perror("sendto"),exit(-1);
    printf("data sent size:%d\n",res);
    char buf[100]={0};
    res=recv(sockfd,buf,sizeof(buf),0);
    if(-1==res)
        perror("recv"),exit(-1);
    printf("data received from server:%s\n",buf);
    res=close(sockfd);
    if(-1==res)
        perror("close"),exit(-1);

    return 0;
}

Copyright © Linux教程網 All Rights Reserved