对比与其他的操作系统,Linux系统有两个主要的特色:免费开源和强大而完善的网络功能,内核大约有百分之十的代码用于支持络.Linux内核支持着各种网络协议,管理着复杂的网络功能,向下提供网络设备的驱动,向上提供应用程序编程网络的接口,即是套接字.当网络数据链路上的数据通过网络适配器(即是网卡)的接收之后,被送往内核的TCP/IP协议栈进行解析(拆包等),在送往应用程序进行处理来与用户进行交互.当然应用程序的数据也会通过套接字送往内核进行网络协议的解析(封装包头等).那么我们如何使用套接字的编程接口去使用内核支持的网络服务呢?下面我们就从TCP的编程开始讲起.
我们都知道传输层在网络的数据传输中肩负着在一个主机中把数据交互给哪个应用程序的任务,提供可靠和不可靠的传输.数据可靠性的保证也是在这一层来完成.有两个主要协议那就是TCP和UDP,TCP是有连接的可靠的传输,而UDP则是无连接的不可靠的传输.TCP一般用于对可靠性要求较高的场合(如:ftp文件传输),而UDP虽然可靠性不高但是由于没有连接所以传输数据的效率很高,用于音视频的传输和一些点对点的文本传输中.
下面将以服务器和客户端的源代码来讲解TCP的编程方法,主要以代码和注释形式讲解.
首先说下TCP编程流程(可以类比于打电话)
服务器:
1.创建流式套接字(socket())————————> 有手机
2.指定本地的网络信息(struct sockaddr_in)———-> 有号码
3.绑定套接字(bind())——————————> 绑定手机
4.监听套接字(listen())—————————->待机
5.链接客户端的请求(accept())———————->接电话
6.接收/发送数据(recv()/send())——————–>通话
7.关闭套接字(close())—————————–>挂机
客户端:
1.创建流式套接字(socket())———————–>有手机
2.指定服务器的网络信息(struct sockaddr_in)——->有对方号码
3.请求链接服务器(connect())———————->打电话
4.发送/接收数据(send()/recv())——————->通话
5.关闭套接字(close())————————— >挂机
********************服务器端代码*********************
//通用的一些头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
//网络编程相关的一些头文件
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define N 32
#define err_log(log)\
do{\
perror(log);\
exit(1);\
}while(0)//错误处理的函数宏
typedef struct sockaddr SA; //通用网络信息结构体
int main(int argc, const char *argv[])
{
struct sockaddr_in serveraddr,clientaddr;
int serverfd,clientfd;
ssize_t bytes;
char buf[N]={0}; //作为发送/接收数据的缓冲区
socklen_t len=sizeof(SA);
//1.创建流式套接字
if((serverfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
err_log("fail to socket:");
}
//2.填充本地网络信息
serveraddr.sin_family=AF_INET; //指定协议族
serveraddr.sin_port=htons(atoi(argv[2])); //指定端口号
serveraddr.sin_addr.s_addr=inet_addr(argv[1]); //指定IP地址
//3.绑定套接字
if(bind(serverfd,(SA*)&serveraddr,len)<0)
{
close(serverfd); //关闭打开的套接字
err_log("fail to bind:");
}
//4.监听套接字
if(listen(serverfd,5)<0)
{
close(serverfd);
err_log("fail to listen:");
}
while(1)
{
//5.链接客户端请求
if((clientfd=accept(serverfd,(SA*)&clientaddr,&len))<0)
{
perror("fail to accept:");
continue; //出错继续链接下一个客户端的请求
}
printf("IP:%s port:%u\n",inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port) ); //打印出被链接上的客户端的网络信息
while(1)
{
bytes=recv(clientfd,buf,sizeof(buf),0);
if(bytes<0) //接收出错处理
{
perror("fail to recv:");
break;
}
if(bytes==0) //对端退出处理
{
puts("peer exit!");
break;
}
printf("client:%s\n",buf); //显示客户端的发送来的信息
send(clientfd,"hello client!",14,0); //发送数据给客户端
}
close(clientfd); //关闭与客户端通信的套接字
}
close(serverfd); //关闭监听套接字
return 0;
}
********************客户器端代码*********************
//通用的一些头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
//网络编程相关的一些头文件
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define N 32
#define err_log(log)\
do{\
perror(log);\
exit(1);\
}while(0)//错误处理的函数宏
typedef struct sockaddr SA; //通用网络信息结构体
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
char buf[N]={0}; //作为发送/接收数据的缓冲区
socklen_t len=sizeof(SA);
//1.创建流式套接字
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
err_log("fail to socket:");
}
//2.填充服务器的网络信息
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
while(1)
{
//3.请求链接服务器
if(connect(sockfd,(SA*)&serveraddr,len)<0)
{
close(sockfd);
err_log("fail to connect:");
}
while(1)
{
//4.发送和接受数据 必须先发送信息给服务器
fgets(buf,sizeof(buf),stdin); //标准输入获得要发送的数据(字符串)
buf[strlen(buf)-1]='\0'; //将输入的‘\n’转化为'\0'字符
send(sockfd,buf,sizeof(buf),0); //发送数据给服务器
bzero(buf,sizeof(buf)); //清空buf缓冲区
recv(sockfd,buf,sizeof(buf),0);//接收服务器发来的数据
printf("server:%s\n",buf);
}
}
close(sockfd); //关闭套接字
return 0;
}
编译这两个代码,执行可执行程序:
本文介绍了Liunx下的TCP的编程,这也是TCP编程的基本步骤,大家需要注意的是:由于TCP是有连接的传输,所以连接之前建立连接(connect和accept来完成的),由于服务器端刚开始是不知道客户端的网络信息的所以必须客户端先主动发生数据给服务器端.
至于这些套接字的编程接口大家可以在man手册中自行查找学习,按照功能 参数和返回值的方式去学习就能掌握这些编程接口,在此就不在赘述.对于本文有什么意见或者更好的编程框架思路希望给予宝贵的意见.