linux: 小试tcp协议

总结 TCP:详细内容在最下面,上面是过段时间的整理心得更加简洁易于复习

使用TCP协议的流程

服务端:socket -> bind -> listen -> accept -> recv -> send -> close
客户端:socket ------------------> connect -> send -> recv -> close

1.socket

/* socket:生成一个套接口描述符  */
int socket(int domain, int type, int protocol);
    //domain ---> AF_INET: IPv4; AF_INET6: IPv6;
    //type ---> SOCK_STREAM: tcp; SOCK_DGRAM: udp;
    //protocol ---> 指定socket传输所用的协议编号,通常为0 。
/*返回值:成功则返回套接口描述符,失败返回-1*/
 

2.bind

/* bind:绑定一个端口号和ip地址,使套接口与端口号和ip地址相关联 */   如果没有手动bind端口号的话,系统也会绑定一个随机端口号,因为随机所以我们无法预知,所以客户端无法找到我们的地址。
int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
    // sockfd为前面socket的返回值。
    // my_addr为结构体指针变量,结构体如下
    // addrlen:sockaddr的结构体长度。通常是sizeof(struct sockaddr);
/*返回值:成功则返回0,失败返回-1*/
/*结构体*/
struct sockaddr  //此结构体不常用
{
    unsigned short int sa_family;  //调用socket()时的domain参数,即AF_INET值。
    char sa_data[14];  //最多使用14个字符长度
};

//使用ipv4时,其socketaddr结构定义便为
struct sockaddr_in  //常用的结构体
{
    unsigned short int sin_family;  //即为sa_family AF_INET
    uint16_t sin_port;  //为使用的port编号
    struct in_addr sin_addr;  //为IP 地址
    unsigned char sin_zero[8];  //未使用
};

struct in_addr
{
    uint32_t s_addr;
};
/*bind实例*/
struct sockaddr_in my_addr;  //定义结构体变量
memset(&my_addr, 0, sizeof(struct sockaddr)); //将结构体清空
    //或bzero(&my_addr, sizeof(struct sockaddr));
my_addr.sin_family = AF_INET;  //表示采用Ipv4网络协议
my_addr.sin_port = htons(8888);
    //表示端口号为8888,通常是大于1024的一个值。
    //htons()用来将参数指定的16位hostshort转换成网络字符顺序
my_addr.sin_addr.s_addr = inet_addr("192.168.1.13");
    // inet_addr()用来将IP地址字符串转换成网络所使用的二进制数字,如果为INADDR_ANY,这表示服务器自动填充本机IP地址。
int iret = bind(sfd, (struct sockaddr*)&my_str, sizeof(struct socketaddr));
if(iret == -1)
{
    perror("bind");
    close(sfd);
    exit(-1);
}
 

注: myaddr.sinport 置为 0,函数会自动为你选择一个未占用的端口来使用。 myaddr.sinaddr.saddr 置为 INADDRANY,系统会自动填入本机IP地址。

3.listen

/* 使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接 */
int listen(int sockfd, int backlog);
    //sockfd: socket描述符
    //backlog: 指定同时能处理的最大连接要求,通常为10或者5。 最大值可设至128
/*返回值:成功则返回0,失败返回-1*/
 

4.accept

/*接受远程计算机的连接请求,建立起与客户机之间的通信连接*/       //此处会返回另外一个套间字用于 和客户端 通讯,也就是说上面绑定的那个套接字是专门用来让客户端连接用的,   通讯使用系统随机分配的一个端
int accept(int sockfd, struct sockaddr * addr, int * addrlen); // 绑定在套接字上专门用来recv和send函数使用,它们也不是一个端口。
    //sockfd: socket描述符
    //addr: 系统会把远程客户端主机的信息(地址和端口号信息)保存到这个指针所指的结构体中
    //addrlen: 表示结构体的长度,为整型指针
/*返回值:成功则返回新的客户端socket描述符new_fd,失败返回-1*/
 

5.recv

/* 用新的套接字来接收远端主机传来的数据,并把数据存到由参数buf 指向的内存空间 */
int recv(int sockfd,void *buf,int len,unsigned int flags);
    //sockfd: socket描述符
    //buf: 接收的字符串存入缓冲区buf中
    //len: 缓冲区长度
    //flags: 通常为0
/*返回值:成功则返回实际接收到的字符数,可能会少于你所指定的接收长度。失败返回-1*/
 

6.send

/* 发送消息到指定IP */
int send(int s,const void * msg,int len,unsigned int flags);
    //sockfd: socket描述符
    //msg: 一般为常量字符串,发出的消息
    //len: msg长度
    //flags: 通常为0
/*返回值:成功则返回实际传送出去的字符数,可能会少于你所指定的发送长度。失败返回-1*/
 

7.close

close(sock_fd) ;

8.connect (in client)

/*用来请求连接远程服务器,将参数sockfd 的socket 连至参数serv_addr 指定的服务器IP和端口号上去*/
int connect(int sockfd,struct sockaddr * serv_addr,int addrlen);
    //sockfd: socket描述符
    //serv_addr: 结构体指针变量,存储着远程服务器的IP与端口号信息
    //addrlen: 结构体变量的长度
/*返回值:成功则返回0,失败返回-1*/
/*connect实例*/
struct sockaddr_in seraddr;    //请求连接服务器
memset(&seraddr, 0, sizeof(struct sockaddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);    //服务器的端口号
seraddr.sin_addr.s_addr = inet_addr("192.168.0.101");  //服务器的ip
if(connect(sfd, (struct sockaddr*)&seraddr, sizeof(struct sockaddr)) == -1)
{
    perror("connect");
    close(sfd);
    exit(-1);
}

使用TCP协议

服务器端

1. 头文件

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

2. socket函数:生成一个套接口描述符。

原型: int socket(int domain,int type,int protocol);
参数: domain?AF_INET: Ipv4网络协议;AF_INET6:IPv6网络协议。 type  tcp:SOCK_STREAM;udp:SOCK_DGRAM。protocol?指定socket所使用的传输协议编号。通常为0。
 
返回值:成功则返回套接口描述符,失败返回-1。
 

3. bind函数:用来绑定一个端口号和IP地址,使套接口与指定的端口号和IP地址相关联。

原型:int bind(int sockfd,struct sockaddr * my_addr,int addrlen);

参数:sockfd 为前面socket的返回值。my_addr 为结构体指针变量 addrlen 为sockaddr的结构体长度。通常是计算sizeof(struct sockaddr)

返回值:成功则返回0,失败返回-1

对于不同的socket domain定义了一个通用的数据结构,如下。注意,此sockaddr结构会因使用不同的socket domain而有不同结构定义。
struct sockaddr  //此结构体不常用
{
    unsigned short int sa_family;  //调用socket()时的domain参数,即AF_INET值。
    char sa_data[14];              //最多使用14个字符长度
};

例如使用AF_INET domain,其socketaddr结构定义如下:

struct sockaddr_in  //常用的结构体
{
    unsigned short int sin_family;  //即为sa_family ?AF_INET
    uint16_t sin_port;              //为使用的port编号
    struct in_addr sin_addr;        //为IP 地址
     unsigned char sin_zero[8];      //未使用
};

其中,struct in_addr 为:

struct in_addr
{
    uint32_t s_addr;
};

4. listen函数:使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。

原型:int listen(int sockfd,int backlog);
 
参数:sockfd为前面socket的返回值,即sfd。backlog指定同时能处理的最大连接要求,通常为10或者5,最大值可设至128。
 
返回值:成功则返回0,失败返回-1

5. accept函数:接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。当accept函数接受一个连接时,会返回一个新的socket标识符,以后的数据传输和读取就要通过这个新的socket编号来处理,原来参数中的socket也可以继续使用,继续监听其它客户机的连接请求。(也就是说,类似于移动营业厅,如果有客户打电话给10086,此时服务器就会请求连接,处理一些事务之后,就通知一个话务员接听客户的电话,也就是说,后面的所有操作,此时已经于服务器没有关系,而是话务员跟客户的交流。对应过来,客户请求连接我们的服务器,我们服务器先做了一些绑定和监听等等操作之后,如果允许连接,则调用accept函数产生一个新的套接字,然后用这个新的套接字跟我们的客户进行收发数据。也就是说,服务器跟一个客户端连接成功,会有两个套接字。)

原型:int accept(int s,struct sockaddr * addr,int * addrlen);
 
参数:s为前面socket的返回值,即sfd。addr为结构体指针变量,和bind的结构体是同种类型的,系统会把远程主机的信息(远程主机的地址和端口号信息)保存到这个指针所指的结构体中。addrlen表示结构体的长度,为整型指针。    
 
返回值:成功则返回新的socket描述符,用于与客户端通信。失败返回-1

6. recv函数:用新的套接字来接收远端主机传来的数据,并把数据存到由参数buf 指向的内存空间。

原型:int recv(int sockfd,void *buf,int len,unsigned int flags);
 
参数:sockfd为前面accept的返回值,即new_fd,也就是新的套接字。buf表示缓冲区。len表示缓冲区的长度。flags通常为0。
 
返回值:成功则返回实际接收到的字符数,可能会少于你所指定的接收长度。失败返回-1。

7. send函数:用新的套接字发送数据给指定的远端主机。

原型:int send(int s,const void * msg,int len,unsigned int flags);
 
参数:s为前面accept的返回值,即new_fd。msg一般为常量字符串。len表示长度。flags通常为0。
 
返回值:成功则返回实际传送出去的字符数,可能会少于你所指定的发送长度。失败返回-1。

8. close函数:当使用完文件后若已不再需要则可使用close()关闭该文件,并且close()会让数据写回磁盘,并释放该文件所占用的资源。

原型:int close(int fd);
 
返回值:若文件顺利关闭则返回0,发生错误时返回-1。
 

小结

1. listen函数使得主动连接套接口变为被动连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。listen函数在一般在调用bind之后,调用accept之前调用,其函数原型如下:

int listen(int sockfd, int backlog)

2. socket函数返回的套接字fd,默认是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认一个套接字是主动连接的,所以需要通过某种方式来告诉系统,而用户进程正是通过listen函数来完成这件事。

3. 当服务器进程处理连接请求时,可能同时还存在其它的连接请求。内核会在自己的进程空间里维护一个队列(客户连接请求队列)用于存放服务器监听到的连接请求的联系方式(端口号与IP)。

4. 服务程序调用accept函数从处于监听状态的流套接字的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。

客户端

connect函数:用来请求连接远程服务器,将参数sockfd 的socket 连至参数serv_addr 指定的服务器IP和端口号上去。

原型:int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
 
参数:sockfd为前面socket的返回值,即sfd。serv_addr为结构体指针变量,存储着远程服务器的IP与端口号信息。addrlen表示结构体变量的长度。
 
返回值:成功则返回0,失败返回-1。
时间: 2025-01-06 01:04:25

linux: 小试tcp协议的相关文章

linux系统TCP协议之Send(转)

tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据. 在阻塞模式下, send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调

Linux下tcp协议socket的recv函数返回时机分析(粘包)

http://www.vckbase.com/index.php/wv/10http://blog.csdn.net/zlzlei/article/details/7689409 文章一: 当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport control protocol,传输控制协议)是面向连接的,提供高可靠性服务.UDP(user datagram pro

Ubuntu利用TCP协议来获取服务器时间

Linux利用TCP协议来获取服务器时间 这里使用Unix网络编程里面的一个小程序,该客户端建立一个到服务器的TCP连接,然后读取由服务器以直观可读格式简单地送回的当前时间和日期. #include "unp.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) err_quit("u

Ubuntu利用TCP协议来获取server时间

Linux利用TCP协议来获取server时间 这里使用Unix网络编程里面的一个小程序,该client建立一个到server的TCP连接,然后读取由server以直观可读格式简单地送回的当前时间和日期. #include "unp.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) err_

linux下磁盘管理神器lvm命令使用以及TCP协议

一.LVM 简介LVM(Logical Volume Manager)是一个应用于Linux的内核的逻辑卷管理器,是Linux环境下对磁盘进行分区管理的一种机制.先介绍几个有关LVM的名词1.PV(物理卷)可以是一个磁盘,一个分区.由PE(物理盘区)组成,多个PV可以组成一个VG(卷组).2.VG(卷组)多个物理卷组成的一个组,但是卷组不可以直接使用,需要在上面创LV(逻辑卷)才可以使用.VG上可以创建多个LV.3.PE(物理盘区)就像我们之前学习的磁盘的block块,默认是4MB.4.LV(逻

TCP协议,UDP协议,Utp,双绞线,DHCP协议,子网掩码,LAN,VLAN,网口,服务器,UI设计,Linux系统,Unix系统,名词解释

TCP协议: TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的.可靠的.基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified).在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议. UDP协议: UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Int

[转帖]Linux TCP/IP协议栈,数据发送接收流程,TCP协议特点

Linux TCP/IP协议栈,数据发送接收流程,TCP协议特点 http://network.51cto.com/art/201909/603780.htm 可以毫不夸张的说现如今的互联网是基于TCP/IP构建起来的网络.弄懂协议栈的原理,无论对调试网络IO性能还是解决网络问题都是有很大帮助的.本片文章就带领大家来看看内核是如何控制网络数据流的. 作者:底层软件架构来源:今日头条|2019-09-30 09:28 收藏 分享 可以毫不夸张的说现如今的互联网是基于TCP/IP构建起来的网络.弄懂

linux基础-TCP/IP协议篇

1.网络层次模型概念介绍:TCP/IP协议就是用于简化OSI层次,以及相关的标准.传输控制协议(tcp/ip)族是相关国防部(DoD)所创建的,主要用来确保数据的完整性及在毁灭性战争中维持通信 是由一组不同功能的协议组合在一起构成的协议族,利用一组协议完成OSI所实现的功能,不单单是指TCP.IP这两个协议. 2.网络层析模型作用说明: OSI七层模型简化四层DoD模型,应用层.表示层.会话层统称为应用层,传输层称为主机到主机层,网络层即为因特网层,数据链路层和网络层统称为网络接入层. 3.网络

tcp协议分析

tcp协议解析 TCP在网络OSI的七层模型中的第四层--Transport层,IP在第三层--Network层,ARP在第二层--Data Link层.在第二层上的数据,我们叫Frame,在第三层上的数据叫Packet,第四层的数据叫Segment. 我们程序的数据首先会打到TCP的Segment中,然后TCP的Segment会打到IP的Packet中,然后再打到以太网Ethernet的Frame中,传到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理 TCP头格式 TCP数据段格