Linux网络编程——简单基于TCP协议的服务器/客户端示例
说明: 本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。 QQ 群 号:513683159 【相互学习】内容来源: 《Linux网络编程》功能描述: 客户端连接服务器后从标准输入读取字符串发送给服务器。 服务器接收到字符串后,发送接收到的总字符串个数给客户端、 客户端将接受到的服务器信息打印
说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《Linux网络编程》
功能描述:
客户端连接服务器后从标准输入读取字符串发送给服务器。
服务器接收到字符串后,发送接收到的总字符串个数给客户端、
客户端将接受到的服务器信息打印到标准输出。
整个过程流程如下:
源文件
服务器端源文件:tcp_server.c
/**
* Step 1 : 初始化工作
* 1.头文件
* 2.宏定义:侦听端口地址与侦听队列长度
* 3.函数声明:服务器对客户端的处理函数,位于:tcp_process.c
* 4.变量:定义并初始化
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORT 8888 //侦听端口地址:8888
#define BACKLOG 2 //侦听队列长度:2
extern void process_conn_server(int s); //服务器对客户端的处理:读取数据并发送响应字符
int main(int argc,char *argv[])
{
int ss = 0; //ss = server socket = 服务器socket描述符
int cs = 0; //cs = client socket = 客户端socket描述符
struct sockaddr_in server_addr; //服务器地址结构
struct sockaddr_in client_addr; //客户端地址结构
int ret = 0; //返回值
pid_t pid; //进程ID
/**
* Step 2 : 建立套接字
*/
ss = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socket
if(ss < 0) //检查是否正常创建socket
{
perror("socket error\n");
exit(EXIT_FAILURE);
}
/**
* Step 3 : 设置服务器地址
* Note:
* htonl():将主机数转换成无符号长整型的网络字节顺序
* htons():将整型变量从主机字节顺序转变成网络字节顺序
*/
bzero(&server_addr,sizeof(server_addr)); //清零
server_addr.sin_family = AF_INET; //设置地址族为AF_INET
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址
server_addr.sin_port = htons(PORT); //设置端口号
/**
* Step 4 : 绑定地址结构到套接字描述符
*/
ret = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));
if (ret < 0) //出错
{
perror("bind error\n");
exit(EXIT_FAILURE);
}
/**
* Step 5 : 设置侦听,侦听队列长度为2,可同时处理两个客户端连接请求
*/
ret = listen(ss,BACKLOG);
if (ret < 0) //出错
{
perror("bind error\n");
exit(EXIT_FAILURE);
}
/**
* Step 6 : 主循环过程
*/
for(;;)
{
/* 接收客户端连接 */
int addrlen = sizeof(struct sockaddr);
cs = accept(ss,(struct sockaddr*)&client_addr,&addrlen);
if(cs < 0) //出错
{
continue; //结束本次循环
}
/* 建立一个新的进程处理到来的连接 */
pid = fork(); //创建新进程
if(pid < 0) /* pid < 0,fork失败 */
{
printf("fork error\n");
}
else if(pid == 0) /* pid = 0,子进程 */
{
close(ss); //子进程中关闭服务器的侦听
process_conn_server(cs); //处理连接
}
else /* pid > 0,父进程 */
{
close(cs); //父进程中关闭客户端的连接
}
}
return 0;
}
客户端源文件:tcp_client.c
/**
* Step 1 : 初始化工作
* 1.头文件
* 2.宏定义:侦听端口地址
* 3.函数声明:服务器对客户端的处理函数,位于:tcp_process.c
* 4.变量:定义并初始化
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8888 //端口地址:8888
extern void process_conn_client(int s);
int main(int argc,char *argv[])
{
int s = 0; //socket描述符
struct sockaddr_in server_addr; //服务器地址结构
int ret = 0; //返回值
/**
* Step 2 : 建立套接字
*/
s = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socket
if(s < 0) //检查是否正常创建socket
{
perror("socket error\n");
exit(EXIT_FAILURE);
}
/**
* Step 3 : 设置服务器地址
*/
bzero(&server_addr,sizeof(server_addr)); //清零
server_addr.sin_family = AF_INET; //设置地址族为AF_INET
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址
server_addr.sin_port = htons(PORT); //设置端口号
/**
* Step 4 : 将用户输入的字符串类型的IP地址转为整型
*/
inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
/**
* Step 5 : 连接服务器
*/
connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
process_conn_client(s); //客户端处理过程
close(s); //关闭连接
}
处理源文件:tcp_process.c
#include <string.h>
#include <stdio.h>
#include <unistd.h>
/* 服务器对客户端处理函数*/
void process_conn_server(int s)
{
ssize_t size = 0;
char buffer[1024]; //数据的缓冲区
for (;;) //循环处理过程
{
size = read(s,buffer,1024); //从套接字中读取数据放到缓冲区buffer中
if(size == 0) //没有数据
{
return;
}
/* 构建响应字符,为接收到客户端字节的数量 */
sprintf(buffer,"%ld 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); //写到标准输出,0表示标准输入,1表示标准输出
}
}
}
Makefile文件
all:client server #all规则,依赖于client和server规则
client:tcp_process.o tcp_client.o #client规则,生成客户端可执行程序
gcc -o client tcp_process.o tcp_client.o
server:tcp_process.o tcp_server.o #server规则,生成服务器端可执行程序
gcc -o server tcp_process.o tcp_server.o
clean: #清理规则,删除client、server和中间文件
rm -f client server *.o
编译并运行
将上面文件全部置于同一目录下。
1️⃣编译:
执行命令:make
,结果如下:
cc -c -o tcp_process.o tcp_process.c #生成tcp_process.o中间文件
cc -c -o tcp_client.o tcp_client.c #生成tcp_client.o中间文件
gcc -o client tcp_process.o tcp_client.o #由tcp_process.o与tcp_client.o生成可执行文件:client
cc -c -o tcp_server.o tcp_server.c #生成tcp_server.o中间文件
gcc -o server tcp_process.o tcp_server.o #由tcp_process.o与tcp_server.o生成可执行文件:server
2️⃣运行:
①打开终端,执行命令:./server
会执行服务端可执行程序server,该程序会在端口8888上侦听,等待客户端的连接请求。
②另起终端,执行命令:./client 127.0.0.1
会执行服务端可执行程序client ,该程序会连接上服务器
紧接着输入字符串就会有相应,效果如下:
hhb@xsndz:/home/hhb/桌面/tcp$ ./client 127.0.0.1
nihao
6 bytes altogether
shiyishi
9 bytes altogether
查询网络连接情况
在程序正常运行的状态,再另起终端,使用netstat
命令查询网络连接情况。
执行指令:netstat
,效果如下:
激活Internet连接 (w/o 服务器)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:39460 localhost:8888 ESTABLISHED
tcp 0 0 localhost:8888 localhost:39460 ESTABLISHED
其中8888为服务器端的端口,39460为客户端的端口
服务器和客户端段通过这两个端口建立了连接。
服务器端与客户端程序设计模式
更多推荐
所有评论(0)