基于TCP的编程
前提:本文基于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 地址家族