1. 程式人生 > >基於TCP的編程

基於TCP的編程

連接服務器 stream socket通訊 port 字符 設置 n) ntop 可靠的

前提:本文基於Linux系統下的學習

服務器端

1 創建通訊端口,返回socket設備的文件描述符 sfd
socket(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:創建一個通訊端點
參數:
domain:
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7
type:
SOCK_STREAM:有序的 可靠的 雙工的 基於連接的 字節流 tcp
SOCK_DGRAM: 基於數據包的 不可靠的 不需要連接的 udp
protocol:0
返回值:
成功 返回一個新的文件描述符
錯誤 -1 errno被設置


2 將sfd和服務器的地址和端口綁定
bind(2)
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:給一個socket綁定一個名字
參數:
sockfd:指定socket,有了地址家族,但沒有具體的地址
addr: 指定具體的地址,根據地址家族采用具體的地址格式
addrlen:是addr所屬結構體的字節數
返回值:
成功 0
錯誤 -1 errno被設置

socklen_t
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];

}

ipv4 ipv6

3 將sfd設置為被動連接模式,監聽客戶端連接的到來.連接到來的時候,將連接放入一個未決連接隊列中.listen(2)
int listen(int sockfd, int backlog);
功能:在指定的socket上監聽連接
參數:
sockfd:指定要被監聽的socket,必須是基於TCP的.
backlog:指定了未決連接隊列的最大值
返回值:
成功 0
錯誤 -1 errno被設置


while(1){
4 從未決連接隊列中取出一個連接進行處理,返回一個和客戶端連接 的文件描述符cfd.阻塞 accept(2)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:從未決連接隊列中獲取第一個未決連接請求,創建一個連接socket.返回一個文件描述符cfd,使用這個cfd和客戶端傳輸數據
參數:
sockfd:指定了在這個socket上獲取第一個未決連接
addr:addr指定的地址空間裏填充了客戶端的地址.具體的格式取決於地址家族.如果為NULL,addrlen也被設置為NULL.
addrlen:指定了addr指向空間的大小
返回值:
錯誤 -1 errno被設置
成功 返回一個非負的文件描述符.處理的未決連接的的文件描述符
5 使用cfd讀取客戶端的請求信息 read(2)
6 處理客戶端的請求信息
7 將處理結果回送給客戶端 write(2)
8 關閉cfd close(2)
}

 1 #include <stdio.h>
 2 #include <sys/types.h>          /* See NOTES */
 3 #include <sys/socket.h>
 4 #include <arpa/inet.h>
 5 #include <string.h>
 6 int main(){
 7     char* msg="hector server..\n";
 8     char buf[128];
 9     char IP[128];
10     struct sockaddr_in serv;//保存服務器的地址和端口
11     struct sockaddr_in clie;//保存客戶端的地址和端口
12     socklen_t clie_len;
13     //創建socket通訊端口,sfd
14     int sfd=socket(AF_INET,SOCK_STREAM,0);
15     if(sfd==-1){
16         perror("socket");
17         return -1;
18     }
19     //初始化serv
20     serv.sin_family=AF_INET;
21     serv.sin_port=htons(7007);
22     serv.sin_addr.s_addr=htonl(INADDR_ANY);
23     //將sfd綁定到到服務器
24     int b=bind(sfd,(struct sockaddr*)&serv,sizeof(serv));
25     if(b==-1){
26         perror("bind");
27         return -1;
28     }
29     
30     //設置為被動監聽狀態,將從客戶端接收的連接放入未決連接中
31     listen(sfd,5);
32     while(1){
33         clie_len=sizeof(clie);
34         //未決連接為空,阻塞
35         //不空,取出第一個連接並處理,cfd
36         int cfd=accept(sfd,(struct sockaddr*)&clie,&clie_len);
37         //客戶端信息放到clie的地址空間
38         printf("%s\n",inet_ntop(AF_INET,&clie.sin_addr,IP,128));
39         
40         //讀取客戶端的消息,顯示出來
41         int r=read(cfd,buf,128);
42         write(1,buf,r);
43         //響應客戶端的信息
44         write(cfd,msg,strlen(msg));
45         close(cfd);
46     }
47     
48     return 0;
49 }

客戶端
1 創建一個通訊端點.socket設備,返回該設備的文件描述符sfd
socket(2)
2 使用sfd連接到服務器.connect(2)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:在socket上發起一個連接
參數:
sockfd:指定socket,在這個socket上發起一個連接請求
addr:指定了目標的地址.具體的格式取決於socket的地址家族
addlen:指定了addr的大小

返回值:
成功 0
錯誤 -1 errno被設置

3 向服務器發送信息
4 等待獲取服務器的處理信息
5 將獲取到的信息處理
6 告知服務器,關閉客戶端的連接. close(2)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
int main(){
    struct sockaddr_in serv;
    char buf[128];
    char* msg="hector client\n";
    //創建socket通訊端口,sfd
    int sfd=socket(AF_INET,SOCK_STREAM,0);    
    if(sfd==-1){
        perror("socket");
        return -1;
    }
    //初始化serv成員
    serv.sin_family=AF_INET;
    serv.sin_port=htons(7007);
    inet_pton(AF_INET,"127.0.0.1",&serv.sin_addr);
    //連接服務器
    int c=connect(sfd,(struct sockaddr *)&serv,sizeof(serv));
    if(c==-1){
        perror("conncet");
        return -1;
    }
    //向服務器發送消息
    write(sfd,msg,strlen(msg));
    //等待服務器的響應
    int r=read(sfd,buf,128);
    write(1,buf,r);
    //關閉
    close(sfd);
    return 0;
}

補充:

struct sockaddr 通用地址家族
{
sa_family_t sa_family;
char sa_data[14];
};

ipv4 地址家族
struct sockaddr_in {
sa_family_t sin_family; /*address family: AF_INET */
in_port_t sin_port; /*port in network byte order */
struct in_addr sin_addr; /* internet address */
};

/* Internet address. */
struct in_addr {
uint32_t s_addr;/*address in network byte order */
};

uint32_t ====== unsigned int

in_port_t======uint16_t======unsigned short int
端口需要網絡字節序 大端
主機字節序 可能是大端,也可能是小端.

主機字節序=======>網絡字節序
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);


ip地址 192.168.1.4 點分十進制的格式 字符串 text
uint32_t binary
text<----->binary
inet_pton(3)
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
功能:text---->binary ipv4|ipv6
參數:
af:
AF_INET: ipv4 sin_addr

AF_INET6:ipv6

src:字符串格式的ip地址
dst:存放轉換結果的首地址
返回值:
成功 1
src 無效 0
af 無效 -1 errno被設置

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
功能:binary----->text
參數:
af:
AF_INET: ipv4
AF_INET6:ipv6
src: binary格式的地址
dst: 存放轉換結果的地址
size: 指定了dst可用空間的字節數
返回值:
錯誤 NULL errno被設置
成功 dst指向的地址

INADDR_ANY 代表本地的所有接口,所有的ip地址.


ipv6 地址家族

基於TCP的編程