TCP通讯程序设计

1、编程模型函数化

函数学习

1)创建套接字

函数名:socket

函数原型:Int socket(int domain,int type,int protocol);

函数功能:创建套接字

所属头文件:<sys/types.h>、<sys/socket.h>

返回值:成功:返回对应于新套接字的文件描述符或者套接字的id,失败:返回错误编码

参数说明:domain:有一系列可取值;type:创建的套接字协议的类型;protocal:协议

解释:

int domain,表示套接字要使用的协议簇,协议簇在文件linux/socket.h有详细定义,常用的协议簇:

AF_UNIX(本机通信);

AF_INET(TCP/IP -IPv4);

AF_INET6(TCP/IP - IPv6);

其中type参数指的是套接字类型;常用的类型有:

SOCK_STREAM(TCP流);

SOCK_DGRAM(UDP数据报);

SOCK_RAW(原始套接字);

最后一个protocol一般设置为0,也就是当确定套接字使用的协议簇和类型时,这个参数的值就为0,但是有时候创建原始套接字时,并不知道要使用的协议簇和类型,也就是domain参数未知情况下,这时protocol这个参数就起作用了,它可以确定协议的种类。
socket是一个函数,那么它也有返回值,当套接字创建成功时,返回套接字,失败返回“-1”,错误代码则写入“errno”中。

2)绑定套接字

解释:Socket套接字地址:是一个数据结构,基于TCP传输协议为例,其数据结构里面包含了:

#include<netinet/in.h>

struct sockaddr_in
{
 unsigned short  sin_family;//地址类型,对于基于TCP/IP传输协议的通信,该值只能是AF_INET;
 unsigned shot int sin_port;//表示端口号,范围在0~65535之间;
 struct in_addr   sin_addr;//32位的IP地址;
 unsigned char  sin_zero[8];//表示填充字节,一般该值为0;
};

例子:

struct sockaddr_in Lewis;
  Lewis.sin_family      = AF_INET;
  Lewis.sin_port        = htons(80);
  Lewis.sin_addr.s_addr = inet_addr("202.96.134.133");
  memset(Lewis.sin_zero,0,sizeof(Lewis.sin_zero));

对于

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);

对于选项INADDR_ANY,就是指的是0.0.0.0的地址,表示所有地址,任意地址,用于一台电脑上有多个网卡的情况,方便管理,管理一个套接字就行,不管数据从哪个网卡过来,只要是绑定的端口号过来的数据,都可以接收;而客户端不能使用INADDR_ANY选项;htons   #include <arpa/inet.h> 
    uint16_t htons(uint16_t hostshort); 
    功能: 将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)

htons 是把你机器上的整数转换成“网络字节序”, 网络字节序是 big-endian,也就是整数的高位字节存放在内存的低地址处。而我们常用的 x86 CPU (intel, AMD) 电脑是 little-endian,也就是整数的低位字节放在内存的低字节处。

举个例子:假定你的port是  0x1234,  在网络字节序里 这个port放到内存中就应该显示成  addr addr+1  0x12 0x34  而在x86电脑上,0x1234放到内存中实际是:  addr addr+1  0x34 0x12 htons 的用处就是把实际内存中的整数存放方式调整成“网络字节序”的方式。

htonl()  #include <arpa/inet.h>

功能:将主机的无符号长整形数转换成网络字节顺序。 
  uint32_t htonl(uint32_t hostlong);

inet_addr() 将一个字符串格式的ip地址转换成一个uint32_t数字格式,但是需要注意的是, 这个函数的返回值在大小端机器上是不同的;

例如输入一个"192.168.0.1"的字符串, 在内存中的排列(字节从低到高) 0xC0, 0xA8 ,0x00 ,0x4A。 那么在小端序机器上,返回的数字就是0x4a00a8c0 , 而在大端序机器上则是0xc0a8004a

函数名:bind

函数原型:Int Bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

函数功能:绑定套接字:将ip地址与套接字相绑定

所属头文件:<sys/types.h>、<sys/socket.h>

返回值:成功:返回对应于新套接字的文件描述符或者套接字的id;失败:返回错误编码

参数说明:sockfd:要绑定套接字的fd;addr:将套接字与那个地址进行绑定;addrlen:地址长度

网络字节序:

3)监听

listen

int listen(int sockfd,int backlog)

作用:1、用来指明服务器已经做好了准备,可以用来接收客户机的请求了。2、用来设置服务器可以接受多少个客户机的连接请求

参数:sockfd与之关联的套接字的fdBacklog:客户机的数目,即允许多少个客户机来进行连接。

成功:返回0失败:返回-1

4)等待连接

Accept

int accept(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict len);

作用:等待客户机来进行连接,如果无客户机来连接,则导致服务器在这里阻塞。

参数:sockfd:创建的套接字的fdAddr:如果有客户机来进行连接,那么通过本地址来返回客户机的地址。Len:接收到客户机的地址的长度

成功:返回新的套接字的描述符。后面在发送和接收数据时用这个新的fd来进行操作。

5)发送数据

函数名:Send

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

作用:发送数据

参数:sockfd:新的套接字的fd,buf:要发送数据的地址,nbytes:要发送数据的长度,flags:标志

 6)接收数据

Recv

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

参数:sockfd:新的套接字的fd,buf:要存数据的地址,nbytes:希望接收多大量的数据,flags:标志

7)关闭连接

Close

8)连接服务器

Connect

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

参数:新创建的套接字的fdaddr:保存的服务器的地址,len:服务器地址的长度

成功:返回0

失败:返回-1

9)

#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);     //将点分十进制的ip地址转化为用于网络传输的数值格式
        返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
 
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);     //将数值格式转化为点分十进制的ip地址格式
        返回值:若成功则为指向结构的指针,若出错则为NULL

 tcp_server.c

修改:

tcp_client.c

原文地址:https://www.cnblogs.com/gary-guo/p/5583552.html