上一篇文章介绍了 TCP/IP相关协议,socket通信流程和涉及到的各种函数:
本篇将具体解释tcp客户端服务器编程模型相关的代码
文章分为4个部分:
1. TCP客户端服务器编程模型流程图
2. 网络字节序与主机字节序
3. TCP编程的地址结构
4. 详细案例代码及解释
一: TCP客户端服务器编程模型流程图
上面两张图片将整个流程已经说明的很清楚了;
二: 网络字节序与主机字节序
字节序即是保存数据的方向方式, 分为 大端存储 和 小端存储;
其中 网络字节序 使用的是大端存储, 而我们用的主机字节序默认采用的小端存储
所以在我们进行网络编程的过程中还需要对相应的数据(地址 端口)进行字节序转换
下面是几个字节序的转换函数:
每个函数都它特定的意思 比如第一张图中的 第一个函数htonl 还有第二张图中的ntop
字符 |
含义 |
h
host(主机)
to
to
n
network
l
long
p
pointer
这样就很好记忆了
三: TCP编程的地址结构
第一个是通用的地址结构
第二个则是封装过的
这两个数据类型可以相互转换
四: 详细案例代码及解释
下面给出一个案例的代码.完成如下功能:
服务器接收来自客户端的连接, 服务器在屏幕输出客户端的地址;
并向客户端发送当前的时间, 客户端再向屏幕输出时间.
服务端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
void print(struct sockaddr_in *addr){
int port2 = ntohs(addr->sin_port);
char ip[16];
memset(ip, 0, sizeof(ip));
inet_ntop(AF_INET, &addr->sin_addr.s_addr, ip, sizeof(ip));
printf("server: (client address: %s(%d) connected)
", ip, port2);
}
void do_service(int cfd){
long t = time(0);
char* s = ctime(&t);
size_t size = strlen(s) * sizeof(char);
if( write(cfd, s, size) != size)
perror("write error");
}
int fd;
void sig_handler(int sig){
if(sig == SIGINT){
close(fd);
exit(1);
}
}
int main(void){
if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
exit(1);
}
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
perror("socket create error");
printf("server: socket created
");
int on = 1;
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
int port = 12345;
sock_addr.sin_port = htons(port);
sock_addr.sin_addr.s_addr = INADDR_ANY;
if(bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0)
perror("bind error");
printf("server: bind OK
");
if(listen(fd, 10) < 0)
perror("listen error");
printf("server: listen OK
");
int client_fd;
struct sockaddr new_addr;
int len = sizeof(new_addr);
if( (client_fd = accept(fd, &new_addr, &len)) < 0 )
perror("accept error");
printf("server: accept OK
");
print((struct sockaddr_in*)&new_addr);
do_service(client_fd);
close(fd);
printf("server: close OK
");
return 0;
}
编译过后打开服务器:
很显然服务器进程当前是阻塞状态(accept), 等待客户端的连接
连接服务器的方式有很多种, 这里我的服务器进程是在桥接的虚拟机中
比如我们可以在本机中打开浏览器用http访问它:
下面是服务器进程得到的信息:
当然为了学习 我们还得完成tcp模型中客户端进程的代码:
#include
#include
#include
#include
#include
#include
#include
const int port = 12345;
const char* ipaddr = "192.168.1.209";
int main(void){
int fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
perror("socket create error");
printf("client: socket created
");
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
inet_pton(AF_INET, ipaddr, &serveraddr.sin_addr.s_addr);
if(connect(fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
perror("connect error");
printf("client: connect OK
");
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
size_t size;
if((size = read(fd, buffer, sizeof(buffer))) < 0)
perror("read error");
printf("client read content: %s", buffer);
close(fd);
return 0;
}
服务端进程及 客户端进程连接后的显示:
好了到此为止 , 我们的案例就完成了..
转载请注明出处:
CSDN_BLOG : AXuanK