Linux socket编程/tcp详解

Socket讲解

服务端客户端通信例子:socket tcp 通信1socket tcp通信2udp使用讲解socket udp通信例子

内容:

    一、简述

    二、创建套接字

    三、服务器

    四、客户端

    五、数据传输

    六、关闭连接

注:可以略过简述,直接看socket的讲解/编程

使用socket进行TCP通信时,经常使用的函数有:将从 二、创建套接字讲起


一、简述

1. 不同主机进程间的通信——Socket

1)网络中的数据传输是一种I/O操作

2)read、write、close操作可应用于Socket描述符

3)Socket是一种文件描述符,代表了一个通信管道的一个端点

4)在Socket类型的文件描述符上,可以完成建立连接,数据传输等操作

5)常用的Socket类型有两种:

流式Socket:SOCK_STREAM , 提供面向连接的Socket

数据报式Socket:SOCK_DGRAM , 提供面向无连接的Socket

2. 字节序

    1)概念:是指多字节数据的存储顺序

    2)分类:

        小端格式:将低位字节数据存储在低地址

        大端格式:将高位字节数据存储在低地址

    3)特点:

网络协议指定了通讯字节序--大端

只有在多字节数据处理时才需要考虑字节序

运行在同一台计算机上的进程相互通信时,一般不用考虑字节序

异构计算机之间通讯,需要转换自己的字节序为网络字节序

4)确定主机字节序:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main()
{
printf("程序开始\n");

union{
short data;
char c[sizeof(short)];
}un;

un.data = 0x0102;

if( un.c[0] == 1 && un.c[1] == 2){
printf("大端格式\n");
}else if( un.c[0] == 2 && un.c[1] == 1){
printf("小端格式\n");
}
printf("程序结束\n");
return;
}

5)字节序转换函数

   1)主机字节序数据转换成网络字节序数据

uint16_t htons(uint16_t host16bit) 把16位值从主机字节序转到网络字节序

uint32_t htonl(uint32_t host32bit)  把32位值从主机字节序转到网络字节序

功能:

    将32或16位主机字节序数据转换成网络字节序数据

参数:

    uint32_t: unsigned int

    hostint32:待转换的32位主机字节序数据

返回值:

    成功:返回网络字节序的值

2)网络字节序数据转换成主机字节序数据

uint16_t ntohs(uint16_t net16bit) 把16位值从网络字节序转到主机字节序

uint32_t ntohs(uint32_t net32bit)  把32位值从网络字节序转到主机字节序

功能:

    将32或16位网络字节序数据转换成主机字节序数据

参数:

    uint32_t: unsignedint

    netint32: 待转换的32位网络字节序数据

返回值:

    成功:返回主机字节序的值

3. 通用套接字地址结构sockaddr

  套接字数据结构用于保存套接字信息,与使用该结构的网络协议有关,每一种网络协议都有其本身的网络地址数据结构,都是以sockaddr_开头的,不同的网络协议有不同的后缀,如IPv4对应的是skocaddr_in

1)地址标识了特定通信域中的套接字端点,

地址格式与特定通信域相关,

为使不同格式地址能被传入套接字函数,地址被强制转换成通用套接字地址结构

      通用套接字地址结构如下:

struct sockaddr
{
  sa_family_t  sa_family; //2 字节,地址族 AF_xxx
  char  sa_data[14]; //14 字节的协议地 址,包含套接字IP和端口号

};

头文件:#include <netinet/in.h>

4. 套接字地址结构sockaddr_in

1)在IPv4因特网域(AF_INET)中,套接字地址结构用sockaddr_in命名、

struct sockaddr_in

{

  sa_family_t  sin_family; //2字节,地址族

  in_port_t  sin_port; //2字节,端口号

  struct in_addr  sin_addr; //4字节,IP地址

  unsigned char  sin_zero[8]; //8字节,

 };

struct in_addr

{

  in_addr_t s_addr; //4字节

};

sin_zero说明:用来将sockaddr_in结构填充到与sockaddr同样长度,可用bzero()或memset()函数将其置为0

头文件:#include <netinet/in.h>

5. 地址转换函数

1)int inet_pton(int family, const char* strptr,void *addrptr);

功能:

将点分十进制数串转换成32位无符号整数

参数:

family 协议族

strptr 点分十进制数串

addrptr 32位无符号整数的地址

返回值:

成功:1

失败:其它

头文件:#include <arpa/inet.h>  

2)const char *inet_ntop(int family, const void* addrptr, char *strptr, size_t len);

 功能:

 将32位无符号整数转换成点分十进制数串

 参数:

 family 协议族

 addrptr 32位无符号整数

 strptr 点分十进制数串

 len strptr缓存区长度

 len的宏定义

 #define INET_ADDRSTRLEN 16

 #define INET6_ADDRSTRLEN 46 //for ipv6

返回值:

成功:则返回字符串的首地址

失败:返回NULL

 头文件:#include <arpa/inet.h>

6.服务模型

TCP:


Udp:

二、创建套接字

创建套接字是进行任何网络通信时必须做的第一步

1.int socket(int family, int type,int protocol);

 功能:

    创建一个用于网络通信的I/O描述符(套接字)

 参数:

 family:协议族

 AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY

 常用值 AF_INET 互联网协议族

 type:套接字类型

SOCK_STREAM(流式套接字)

SOCK_DGRAM(数据包套接字)   

SOCK_RAW (原始套接字)

SOCK_SEQPACKET

protocol:协议类别

0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP

常用值 0

返回值:套接字

socket创建的套接字特点

使用socket创建套接字时,系统不会分配端口

使用socket创建的是主动套接字,但作为服务器,

需要被动等待别人的连接

头文件:#include<sys/socket.h>

   

三、服务端

做为服务器需要具备的条件

    1)具备一个可以确知的地址,以便让别人找到我

    2)让操作系统知道你是一个服务器,而不是一个客户端

    3)等待连接的到来

对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始

1.int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);

 功能:

    将本地协议地址与sockfd绑定,套接字配置

 参数:

    sockfd: socket套接字

    myaddr: 指向特定于协议的地址结构指针,

    addrlen:该地址结构的长度

 返回值:

    成功:返回0

    失败:其他

头文件:#include<sys/socket.h>

myaddr说明:

    myaddr其实是指向含有本机IP地址及端口号等信息的sockaddr类型的指针

    my_addr.sin_port= 0;//系统随机选择一个未使用的端口

    my_addr.sin_addr= INADDR_ANY;//填入本机IP地址

注意:在使用bind函数使,需要将sin_port转换为网络字节优先

   

注意:INADDR_ANY 通配地址,值为0

2. int listen(int sockfd, int backlog);

功能: 

listen使套接字处于被动监听模式

 使操作系统为该套接字设置一个连接队列,来记录所有连接到该套接字的连接

参数:

 sockfd: socket监听套接字

 backlog:连接队列的长度,输入队列已满时,将拒绝连接请求

返回值:

 成功:返回0

 失败:其他

头文件:#include <sys/socket.h>

4.int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);

功能:

从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待

在建立好输入队列后,服务器就调用accept函数,然后进入睡眠并等待客户端的连接请求

收到连接请求时,sockfd将将建立一个新的套接字,并把新套接字和请求连接进程的地址联系起来,收到服务套接字的初始套接字仍可以继续在以前的套接字上监听,同时也可以在新的套接字描述符上进行数据传输

参数:

 sockfd: socket监听套接字

 cliaddr: 用于存放客户端套接字地址结构

 addrlen:套接字地址结构体长度

返回值:已连接套接字

注意:accept函数返回的是一个已连接套接字,这个套接字代表当前这个连接

头文件:#include <sys/socket.h>

四、客户端

作为客户端需要具备的条件:知道服务器的IP地址以及端口号

1.int connect(int sockfd,const struct sockaddr* addr,socklen_t len);

  功能:

主动跟服务器建立链接

连接建立成功后才可以开始传输数据(对于TCP协议)

用于tcp客户端,udp不使用

  参数:

sockfd:socket套接字

addr: 需连接的服务器地址结构

addrlen:地址结构体长度

返回值:

成功:0

失败:其他

注意:connect函数建立连接之后不会产生新的套接字

头文件:#include <sys/socket.h>

例子:

五、数据传输

当连接建立后,通信的两端便具备两个套接字

套接字也是一种文件描述符,所以read、write函数可以用于从这个连接中取出或向其写入数据

1.ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);

功能:

  用于发送数据

  注意:不能用TCP协议发送0长度的数据包

参数:

sockfd: socket套接字

buf: 待发送数据缓存区的地址

nbytes: 发送缓存区大小(以字节为单位)

flags: 套接字标志(常为0)

返回值:成功发送的字节数

头文件:#include <sys/socket.h>

注意:send发送的字节数可能和请求的字节数不一致,或小或大

2.ssize_t recv(int sockfd, void *buf,size_t nbytes,int flags);

功能:

用于接收网络数据

参数:

sockfd: 套接字

buf: 指向接收网络数据的缓冲区

nbytes: 接收缓冲区的大小(以字节为单位)

flags: 套接字标志(常为0)

返回值:成功接收到字节数

头文件:#include <sys/socket.h>

六、关闭连接

1. int close(int fd);

参数:fd是调用socket函数返回的套接字描述符

头文件:#include<unistd.h>

2. int shutdown(int s,int how)

功能:

    允许只停止某个方向的数据传输,另一个方向的继续传输

参数:

    s 需要关闭的套接字描述符

    how:

0 停止继续接收数据

1 停止继续发送数据

2 停止发送和接收数据

返回值:

成功 0

失败 -1

头文件: #include <sys/socket.h>

3.使用close函数即可关闭套接字

    1)关闭一个代表已连接套接字,将导致另一端接收到一个0长度的数据包

    2)做服务器时

      关闭socket创建的监听套接字,将导致服务器无法继续接受新的连接,但不      会影响已经建立的连接

      关闭accept返回的已连接套接字,将导致它所代表的连接被关闭,但不会影      响服务器的监听

    3)做客户端时

        关闭连接就是关闭连接,不意味着其他


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Amarao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值