1. 程式人生 > >linux 網絡編程

linux 網絡編程

例子 blog soc ems 二進制 sel dto print msg_oob

linux網絡編程中主要分為服務器和客戶端兩部分,而網絡編程中又分為TCP和UDP兩種。
TCP(傳輸控制協議)和UDP(用戶數據報協議是網絡體系結構TCP/IP模型中傳輸層一層中的兩個不同的通信協議。
TCP:傳輸控制協議,一種面向連接的協議,給用戶進程提供可靠的全雙工的字節流,TCP套接口是字節流套接口(stream socket)的一種。
UDP:用戶數據報協議。UDP是一種無連接協議。UDP套接口是數據報套接口(datagram socket)的一種。

============================================TCP=======================================


A、建立服務器的步驟(TCP):
(1)創建TCP套接字: socket(int domain, int type, int protocol);

(2)設置端口號和IP: struct sockaddr_in (本結構體包含端口號和IP等信息)

(3)綁定套接字與網絡地址 : bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

(4)將待連接套接字設置為監聽套接字,並設置最大同時接收連接請求個數:listen(int socket, int backlog);

(5)等待客戶端連接的請求:accept

(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

(6)進行通信:recv(int sockfd, void *buf, size_t len, int flags);或用read()也可以

(7)關閉套接字:int close(int fildes);


B、建立客戶端(TCP):
(1)創建TCP套接字:socket(int domain, int type, int protocol);

(2)設置服務器的IP與端口號:struct sockaddr_in (本結構體包含端口號和IP等信息)

(3)發送連接請求:connect

(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

(4)進行通信:send(int sockfd, const void *buf, size_t len, int flags);或用write()也可以

(5)關閉套接字:int close(int fildes);


==========================================UDP========================================
A、建立服務器的步驟(UDP):
(1)創建UDP套接字: socket(int domain, int type, int protocol);

(2)設置端口號和IP: struct sockaddr_in (本結構體包含端口號和IP等信息)

(3)綁定套接字與網絡地址 : bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

(4)進行通信:recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

(5)關閉套接字:int close(int fildes);

B、建立客戶端(UDP):
(1)創建UDP套接字: socket(int domain, int type, int protocol);

(2)設置端口號和IP: struct sockaddr_in (本結構體包含端口號和IP等信息)

(3)進行通信:sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

(4)關閉套接字:int close(int fildes);



一、創建套接字

1、頭文件 #include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

2、接口聲明 int socket(int domain, int type, int protocol);
參數:
a、domain:
AF_INET / PF_INET :網際協議
AF_UNIX / PF_UNIX :本地協議

b、type:
SOCK_STREAM :流式套接字(TCP)
SOCK_DGRAM :數據報套接字(UDP)

c、protocol:協議
一般寫為0 :當protocol為0時,會自動選擇type類型對應的默認協議。

3、返回值:
成功:等待連接的套接字
失敗:-1


二、綁定套接字與網絡地址

1、頭文件 #include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

2、接口聲明 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

參數:
a、sockfd:等待連接套接字
b、addr:包含本地地址(IP+PORT)的通用地址結構體的指針
c、addrlen:地址結構體大小

3、返回值:
成功:0
失敗:-1


三、將待連接套接字設置為監聽套接字,並設置最大同時接收連接請求個數

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 int listen(int socket, int backlog);
參數:
a、sockfd:待連接套接字
b、最大同時連接請求個數

3、返回值:
成功:0,並將sockfd設置為監聽套接字
失敗:-1

四、等待對端連接請求

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

參數:
a、sockfd:經過listen()之後的監聽套接字
b、addr:本來為(struct sockaddr_in)型的結構體,但是寫進來的時候要強轉成為(struct sockaddr *)型

c、addrlen:為(struct sockaddr_in)結構體的大小


3、返回值:
成功:返回一個非負整數的文件描述符
失敗-1


五、連接對端監聽套接字

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

參數:
a、sockfd:待連接的套接字
b、addr:本來為(struct sockaddr_in)型的結構體,但是寫進來的時候要強轉成為(struct sockaddr *)型
c、addrlen:為(struct sockaddr_in)結構體的大小

3、返回值:
成功:0
失敗:-1


六、斷開本端連接套接字

1、頭文件 #include <unistd.h>

2、接口聲明 int close(int fd);
參數:
a、fd:文件描述符

3、返回值:
成功:0
失敗:-1


七、斷開本端連接套接字

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 int shutdown(int sockfd, int how);
參數:
a、sockfd:accept()成功之後的已連接套接字(文件描述符)

b、how:斷開方式
SHUT_RD
Disables further receive operations. //關閉讀端

SHUT_WR
Disables further send operations. //關閉寫端

SHUT_RDWR
Disables further send and receive operations. //關閉讀寫端

3、返回值:
成功:0
失敗:-1


八、將文本地址轉化為二進制地址

1、頭文件 #include <arpa/inet.h>

2、接口聲明 int inet_pton(int af, const char *src, void *dst);
參數:
a、af:地址族:
AF_INET:ipv4地址
AF_INET6:ipv6地址

b、src:指向“點分式”ipv4或ipv6地址的指針:例如:192.168.1.105

c、dst:類型為(struct in_addr *)的指針

3、返回值:
成功:1
失敗:0代表地址與地址族不匹配,-1代表地址不合法

九、將二進制地址轉化為文本地址

1、頭文件 #include <arpa/inet.h>

2、接口聲明 const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);

參數:
a、af:地址族:
AF_INET:ipv4地址
AF_INET6:ipv6地址

b、src:指向“點分式”ipv4或ipv6地址的指針:例如:192.168.1.105

c、dst:地址緩沖區指針

d、size:地址緩沖區大小

3、返回值:
成功:returns a non-NULL pointer to dst.
失敗:NULL


十、向TCP套接字發送數據

1、頭文件IS #include <sys/types.h>
#include <sys/socket.h>


2、接口聲明 ssize_t send(int sockfd, const void *buf, size_t len, int flags);
參數:
a、sockfd:已連接的套接字

b、buf:即將被發送的數據

c、len:數據長度

d、flags:發送標誌(可以填0)
MSG_EOR:當對端已關閉時,不產生SIGPIPE信號
MSG_OOB :發送緊急(帶外)數據,只針對TCP連接

3、返回值:
成功:已發送字節數
失敗:-1

備註當flags為0時,send與write作用一樣,所以TCP也可以用write發送數據

十一、從TCP套接字接收數據

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
參數:
a、sockfd:已連接套接字

b、buf:存儲數據緩沖區

c、len:緩沖區大小

d、flags:接收標誌(可以填0)

3、返回值:
成功:已接收字節數
失敗:-1
備註當flags為0時,recv與read作用一樣,所以TCP也可以用read來接收數據


十二、向UDP套接字發送數據

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
參數:
a、sockfd:UDP(套接字) (SOCK_DGRAM :數據報套接字)

b、buf:即將被發送的數據

c、len:數據長度

d、flags:發送標誌:與send函數的flags一樣

e、dest_addr:對端網絡地址

f、addr_len:地址長度 (struct sockaddr_in 的大小)

3、返回值:
成功:已發送字節數
失敗:-1

備註:
當dest_addr為NULL,aaddrlen為0時,sendto與send作用一致


十三、從UDP套接字接收數據

1、頭文件 #include <sys/types.h>
#include <sys/socket.h>

2、接口聲明 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);

參數:
a、sockfd:UDP(套接字) (SOCK_DGRAM :數據報套接字)

b、buf:存儲數據緩沖區

c、len:緩沖區大小

d、flags:接收標誌:與send函數的flags一樣

e、dest_addr:對端網絡地址

f、addr_len:地址長度 (struct sockaddr_in 的大小)

3、返回值:
成功:已接收字節數
失敗:-1


十四、多路復用

1、頭文件 #include <sys/select.h>

2、接口聲明 int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
參數:
a、nfds:所有正在監測的套接字的最大值加1

b、readfds:讀就緒文件描述符集合

c、writefds:寫就緒文件描述符集合

d、execeptfds:異常就緒文件描述符集合

e、timeout:超時控制

3、返回值:
成功:就緒文件描述符總數(當超過返回時為0)
失敗-1


超時設置:
The timeout
The time structures involved are defined in <sys/time.h> and look like

struct timeval {
long tv_sec; /* seconds */ 秒
long tv_usec; /* microseconds */ 微秒
};

and

struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */ 納秒
};


文件描述符集合操作函數:
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

下面是簡單的例子,服務器負責接收數據,客戶端負責發送數據。

================================下面為TCP的代碼=====================

1、服務器:service.c

#include <sys/types.h>          
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h>
#include <stdlib.h>
#include <string.h>

#define IP "192.168.1.104"   //這是我的虛擬機上的IP,讀者要改成自己的IP
#define PORT 50001     //端口號(可改)
#define LISTEN_MAX 8   //最大同時接收連接請求個數
#define LENGTH 100     //數組的大小

int main(int argc,char *argv[])
{
    int sockfd; 
    char buf[LENGTH];
    //1、創建TCP套接字(SOCK_STREAM為TCP),AF_INET為ipv4
    sockfd = socket(AF_INET,SOCK_STREAM,0);  
    if(sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    
    //2、設置端口號和IP
    struct sockaddr_in srvaddr,cliaddr;
    socklen_t len = sizeof(cliaddr);
    srvaddr.sin_family = AF_INET;    //ipv4
    srvaddr.sin_port = htons(PORT);  //端口號
    inet_pton(AF_INET,IP,&srvaddr.sin_addr); //將文本地址轉化為二進制地址
    
    
    //3、綁定套接字與網絡地址
    int ret = bind(sockfd,(struct sockaddr *)&srvaddr,len); 
    if(ret == -1)
    {
        perror("bind");
        exit(1);
    }
    
    //4、將待連接套接字設置為監聽套接字,並設置最大同時接收連接請求個數4、
    int ret1 = listen(sockfd,LISTEN_MAX); 
    if(ret1 == -1)
    {
        perror("listen");
        exit(1);
    }
    
    //5、等待客戶端端連接的請求,這是阻塞的
    int connfd = accept(sockfd,(struct sockaddr*)&cliaddr,&len); 
    if(connfd == -1)
    {
        perror("accept");
        exit(1);
    }
    
    
    while(1)
    {
        memset(buf,0,sizeof(buf));
        recv(connfd,buf,sizeof(buf),0); //讀出客戶端發送過來的數據,這裏可以用read代替
        printf("from client:%s",buf);
        
        if(strncmp(buf,"quit",4) == 0)
        {
            break;
        }
    }
    
    close(connfd);
    close(sockfd);
}

2、客戶端:client.c

#include <sys/types.h>          
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define IP "192.168.1.104"   //這是我的虛擬機上的IP,讀者要改成自己的IP
#define PORT 50001    //端口號(要跟服務器上的一致)
#define LENGTH 100 //數組大小
int main(int argc,char *argv[])
{
    char buf[LENGTH];
    
    //1、創建TCP套接字(SOCK_STREAM為TCP),AF_INET為ipv4
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    
    
    //2、設置服務器的IP與端口號
    struct sockaddr_in srvaddr;
    socklen_t len = sizeof(srvaddr);
    srvaddr.sin_family = AF_INET;  //ipv4
    srvaddr.sin_port = htons(PORT);  //端口號
    inet_pton(AF_INET,IP,&srvaddr.sin_addr); //將文本地址轉化為二進制地址
    
    
    //3、發送連接請求,這是阻塞的
    int ret = connect(sockfd,(struct sockaddr*)&srvaddr,len); 
    if(ret == -1)
    {
        perror("connect");
        exit(1);
    }
    
    while(1)
    {
        memset(buf,0,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);
        send(sockfd,buf,strlen(buf),0); //向服務器發送數據,這裏可以用write代替
        
       if(strncmp(buf,"quit",4) == 0)
       {
           break;
       }
    }
    
    close(sockfd);
    return 0;
}


================================下面為UDP的代碼=====================

1、服務器:service.c

#include <sys/types.h>          
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define IP "192.168.1.104"   //這是我的虛擬機上的IP,讀者要改成自己的IP
#define PORT 50002 //端口號
#define LENGTH 100 //數組大小


int main(int argc,char *argv[])
{
    // 1、創建UDP套接字 ,AF_INET為iPv4,SOCK_DGRAM為UDP
    int sockfd = socket(AF_INET,SOCK_DGRAM,0) ;
    if(sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    
   //2、設置端口號和IP
   struct sockaddr_in  srvaddr,cliaddr;
   socklen_t len = sizeof(cliaddr);
   srvaddr.sin_family = AF_INET;
   srvaddr.sin_port = PORT;
   inet_pton(AF_INET,IP,&srvaddr.sin_addr); //將文本地址轉化為二進制地址
   
   //3、綁定套接字與網絡地址
  int ret = bind(sockfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr));
   if(ret == -1)
   {
       perror("bind");
       exit(1);
   }
   
   char buf[LENGTH];
   while(1)
   {
       memset(buf,0,sizeof(buf));
       recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&cliaddr,&len);
       printf("from client:%s",buf);
       if(strncmp(buf,"quit",4) == 0)
       {
           break;
       }
   }
   
   close(sockfd);
   return 0;
   
}

2、客戶端:client.c

#include <sys/types.h>          
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define IP "192.168.1.104"   //這是我的虛擬機上的IP,讀者要改成自己的IP
#define PORT 50002     //端口號(要跟服務器上的一致)
#define LENGTH 100 //數組大小

int main(int argc,char *argv[])
{
    //1、創建UDP套接字(SOCK_DGRAM為UDP),AF_INET為ipv4
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    
    //2、設置對方的IP與端口號
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = PORT;
    inet_pton(AF_INET,IP,&srvaddr.sin_addr);//將文本地址轉化為二進制地址
    
    char buf[LENGTH];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&srvaddr,sizeof(srvaddr));
        if(strncmp(buf,"quit",4) == 0)
        {
            break;
        }
    }
    
    close(sockfd);
}

linux 網絡編程