Linux网络编程--TCP网络编程基础(简单的server/client模型)
2015-08-24 15:25
861 查看
本文主要讲解C/S模型,对服务器端和客户端的流程和函数的使用进行解析,以及网络编程中对信号的处理,特别是由于连接关闭而产生的SIGPIPE信号和终止进程而产生的SIGINT信号,当然截取信号并进行处理是程序稳定性的基本要求。
TCP网络编程中常用的函数主要有:socket(),bind(),listen(),accept(),read(),write(),connect(),close();服务器端和客户端使用不同的函数,流程的差异也是很大;其中服务器端的程序设计需要依次调用socket(),bind(),listen(),accept(),close()函数,客户端程序设计需要依次调用socket(),connect(),close()函数。那么这些函数的具体定义和使用方法就不再细讲,网上随便一搜很多很多,最好是使用官方的man手册进行查询和使用。
下面主要介绍一个简单的基于TCP协议的服务器/客户端的例子,通过本例中代码和程序构建过程的了解,学习者可以对TCP协议的服务器、客户端程序的设计方法和过程有所了解,能够进一步编写自己的程序。
功能描述:程序分为服务器端和客户端,客户端连接服务器之后从标准输入设备读取输入的字符发给服务器;服务器端收到字符串后,发送接受到的总字符数给客户端;客户端将接收到的服务端的信息打印到标准输出。框架图如下:
服务器网络程序:tcp_server.c
客户网络程序:tcp_client.c
服务端和客户端中对字符串以及信号处理功能的代码:tcp_process.c
程序的编译运行文件:Makefile
TCP网络编程中常用的函数主要有:socket(),bind(),listen(),accept(),read(),write(),connect(),close();服务器端和客户端使用不同的函数,流程的差异也是很大;其中服务器端的程序设计需要依次调用socket(),bind(),listen(),accept(),close()函数,客户端程序设计需要依次调用socket(),connect(),close()函数。那么这些函数的具体定义和使用方法就不再细讲,网上随便一搜很多很多,最好是使用官方的man手册进行查询和使用。
下面主要介绍一个简单的基于TCP协议的服务器/客户端的例子,通过本例中代码和程序构建过程的了解,学习者可以对TCP协议的服务器、客户端程序的设计方法和过程有所了解,能够进一步编写自己的程序。
功能描述:程序分为服务器端和客户端,客户端连接服务器之后从标准输入设备读取输入的字符发给服务器;服务器端收到字符串后,发送接受到的总字符数给客户端;客户端将接收到的服务端的信息打印到标准输出。框架图如下:
服务器网络程序:tcp_server.c
#include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <linux/in.h> #include <signal.h> extern void sig_proccess(int signo); #define PORT 8888 /* 侦听端口地址 */ #define BACKLOG 2 /* 侦听队列长度 */ int main(int argc, char *argv[]) { int ss,sc; /* ss为服务器的socket描述符,sc为客户端的socket描述符 */ struct sockaddr_in server_addr; /* 服务器地址结构 */ struct sockaddr_in client_addr; /* 客户端地址结构 */ int err; /* 返回值 */ pid_t pid; /* 分叉的进行id */ signal(SIGINT, sig_proccess); signal(SIGPIPE, sig_proccess); /* 建立一个流式套接字 */ ss = socket(AF_INET, SOCK_STREAM, 0); if(ss < 0){/* 出错 */ printf("socket error\n"); return -1; } /* 设置服务器地址 */ bzero(&server_addr, sizeof(server_addr)); /* 清0 */ server_addr.sin_family = AF_INET; /* 协议族 */ server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */ server_addr.sin_port = htons(PORT); /* 服务器端口 */ /* 绑定地址结构到套接字描述符 */ err = bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr)); if(err < 0){/* 出错 */ printf("bind error\n"); return -1; } /* 设置侦听 */ err = listen(ss, BACKLOG); if(err < 0){/* 出错 */ printf("listen error\n"); return -1; } /* 主循环过程 */ for(;;) { int addrlen = sizeof(struct sockaddr); /* 接收客户端连接 */ sc = accept(ss, (struct sockaddr*)&client_addr, &addrlen); if(sc < 0){ /* 出错 */ continue; /* 结束本次循环 */ } /* 建立一个新的进程处理到来的连接 */ pid = fork(); /* 分叉进程 */ if( pid == 0 ){ /* 子进程中 */ close(ss); /* 在子进程中关闭服务器的侦听 */ process_conn_server(sc);/* 处理连接 */ }else{ close(sc); /* 在父进程中关闭客户端的连接 */ } } }
客户网络程序:tcp_client.c
#include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <linux/in.h> #include <signal.h> extern void sig_proccess(int signo); extern void sig_pipe(int signo); static int s; void sig_proccess_client(int signo) { printf("Catch a exit signal\n"); close(s); exit(0); } #define PORT 8888 /* 侦听端口地址 */ int main(int argc, char *argv[]) { struct sockaddr_in server_addr; /* 服务器地址结构 */ int err;/* 返回值 */ signal(SIGINT, sig_proccess); signal(SIGPIPE, sig_pipe); /* 建立一个流式套接字 */ s = socket(AF_INET, SOCK_STREAM, 0); if(s < 0){/* 出错 */ printf("socket error\n"); return -1; } /* 设置服务器地址 */ bzero(&server_addr, sizeof(server_addr)); /* 清0 */ server_addr.sin_family = AF_INET; /* 协议族 */ server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */ server_addr.sin_port = htons(PORT); /* 服务器端口 */ /* 将用户输入的字符串类型的IP地址转为整型 */ inet_pton(AF_INET, argv[1], &server_addr.sin_addr); /* 连接服务器 */ connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); process_conn_client(s); /* 客户端处理过程 */ close(s); /* 关闭连接 */ }
服务端和客户端中对字符串以及信号处理功能的代码:tcp_process.c
#include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> /* 服务器对客户端的处理 */ void process_conn_server(int s) { ssize_t size = 0; char buffer[1024]; /* 数据的缓冲区 */ for(;;){/* 循环处理过程 */ /* 从套接字中读取数据放到缓冲区buffer中 */ size = read(s, buffer, 1024); if(size == 0){/* 没有数据 */ return; } /* 构建响应字符,为接收到客户端字节的数量 */ sprintf(buffer, "%d bytes altogether\n", size); write(s, buffer, strlen(buffer)+1);/* 发给客户端 */ } } /* 客户端的处理过程 */ void process_conn_client(int s) { ssize_t size = 0; char buffer[1024]; /* 数据的缓冲区 */ for(;;){/* 循环处理过程 */ /* 从标准输入中读取数据放到缓冲区buffer中 */ size = read(0, buffer, 1024); if(size > 0){/* 读到数据 */ write(s, buffer, size); /* 发送给服务器 */ size = read(s, buffer, 1024);/* 从服务器读取数据 */ write(1, buffer, size); /* 写到标准输出 */ } } } void sig_proccess(int signo) { printf("Catch a exit signal\n"); exit(0); } void sig_pipe(int sign) { printf("Catch a SIGPIPE signal\n"); /* 释放资源 */ }
程序的编译运行文件:Makefile
CC = gcc all:client server client:tcp_process.o tcp_client.o $(CC) -o client tcp_process.o tcp_client.o server:tcp_process.o tcp_server.o $(CC) -o server tcp_process.o tcp_server.o clean: rm -f *.o
相关文章推荐
- 使用 cURL 进行 HTTP 请求实例
- 使用net模块实现基于TCP的数据通信
- Chapter 4 分布式(网络)存储系统
- Android 网络--我是怎么做的: Volley+OkHttp+Https
- java代码实现如jsp页面的form请求方式二HttpClient
- 又见GCD(http://acm.hdu.edu.cn/showproblem.php?pid=2504)
- Neural Networks for Machine Learning by Geoffrey Hinton (4)
- 调试打印ethhdr,iphdr,tcphdr和指定长度的内存块
- 计算机应用——DOS命令下的网络管理和诊断-终
- 计算机应用——DOS命令下的网络管理和诊断-续
- HTTP状态码(HTTP Status Code)
- Util:跟网络相关的工具类
- 黑马程序员----Java网络编程
- golang net/http包使用
- httpd详解(二)
- SCST在SAN网络环境中的应用
- 通过扩展让ASP.NET Web API支持JSONP -摘自网络
- DNN结构演进History—LSTM网络
- httpd: unrecognized service 的解决方案
- tcpdump summary