网络编程(socket套接字)
相比于依赖Linux内核的IPC——管道、消息队列、共享内存、信号、信号量来说,socket适用于多机通信。
1.相关概念:
网络字节序:
字节序有Little endian小端字节序 和 Big endian大端字节序,网络字节序属于大端字节序。小端字节序是将低字节序存储在起始地址,大端字节序是将高字节序存储在起始地址。
IP地址和端口号:
IP地址用来在网络连接中标识主机,相当于电话的电话号码,一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。这些服务都是通过IP地址来实现的。主机区分网络服务不能只靠IP地址,因为IP地址于网络服务的关系是一对多的关系。实际上通过IP地址加端口号来区分不同的服务。端口号提供访问通道,服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69;
TCP/HTTP/UDP协议:数据传输协议,TCP面向连接,用于对数据要求高(发送控制指令)。UDP面向报文,用于大数据量的传输。
TCP/UDP对比
1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发数据之前不需要建立连接。
2.TCP提供可靠的服务。通过TCP连接传送到数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3.TCP面向字节流,实际上是TCP把数据看成一串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5.TCP首部开销20字节;UDP的首部开销小,只有8个字节
6.TCP 的逻辑通信信道是全双工的可靠通信,UDP则是不可靠信道
网络编程步骤和API
服务端TCP server
1.int socket(int domain, int type, int protocol);//创建套接字
2.int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//绑定IP地址和端口号
3.int listen(int sockfd, int backlog);//监听网络连接
4.accept()//等待客户端的介入
5.read,write//数据交互
6.int close(int sockfd);//关闭套接字,断开网络连接
客户端TCP client
1.socket()//创建套接字
2.connect()//
3.read,write//数据交互
4.close()//关闭套接字
实现双方聊天
以下demo都是先执行服务端,后执行客户端,程序在shell执行是设定为三个参数,第一个参数为路径与程序名,第二个参数是在同一网段可ping的IP地址,第三个参数为自设的端口号
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define RL 1024
int main(int argc,char **argv)
{
if(argc != 3)
{
perror("参数不正确。\n");
exit(-1);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>
//定义套接字和bind配置需要的结构体
int s_fd;
int c_fd;
char readbuf[RL];
char writebuf[RL];
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
//1.socket创建套接字
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
printf("创建套接字失败!\n");
perror("原因:");
exit(-1);
}else{
printf("IP:%s Prot:%s\n",argv[1],argv[2]);
}
//2.bind绑定IP地址和端口号,配置网络协议
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen监听客户端连接请求(三次握手)
listen(s_fd,7);
//4.accept连接完成三次握手的客户端
int clen=sizeof(struct sockaddr_in);
while(1)
{
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1)
{
printf("客户端连接失败!\n");
exit(-1);
}else{
printf("客户端连接进入...\n");
printf("客户端IP:%s\n",inet_ntoa(c_addr.sin_addr));
printf("客户端连接成功。\n");
}
//5.read write与客户端的数据交互
if(fork()==0)
{
pid_t ip;
ip=fork();
if(ip==0)
{
while(1)
{
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
printf(">收到信息:%s\n",readbuf);
if(strstr(readbuf,"quit"))break;
}
}else if(ip>0){
while(1)
{
memset(writebuf,0,sizeof(writebuf));
printf(">");
gets(writebuf);
write(c_fd,writebuf,strlen(writebuf));
}
}else{
printf("连接客户端失败!\n");
}
}
}
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define RL 124
int main(int argc,char **argv)
{
if(argc != 3)
{
perror("参数不正确。\n");
exit(-1);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>
//定义套接字和bind配置需要的结构体
int c_fd;
char readbuf[RL];
struct sockaddr_in c_addr;
//1.socket创建套接字
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
printf("创建套接字失败!\n");
perror("原因:");
exit(-1);
}else{
printf("IP:%s Prot:%s\n",argv[1],argv[2]);
}
//2.connect连接服务端
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in))==-1)
{
printf("连接服务端失败!\n");
perror("原因:");
exit(-1);
}else{
printf("连接服务端成功。\n");
}
//3.read write与客户端的数据交互
if(fork()==0)
{
while(1)
{
memset(readbuf,0,sizeof(readbuf));
printf("input:");
gets(readbuf);
write(c_fd,readbuf,sizeof(readbuf));
}
}
while(1)
{
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
printf("收到信息:%s\n",readbuf);
}
//if(strstr(readbuf,"quit"))break;
return 0;
}
FTP服务器
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define RL 10240
#define FORBID 0
char readbuf[RL]={0};
char writebuf[64]={0};
//客户端文件上传到服务端的功能函数
int uploadFunction(int c_fd)
{
FILE *hafile;
memset(readbuf,0,sizeof(readbuf));
char *file=writebuf+strlen("upload ");
printf("我要上传:%s||%s\n",file,readbuf);
hafile=fopen(file,"r");
if(hafile==NULL)
{
printf("上传%s失败,文件是否存在?是否有执行权限!\n",file);
return -1;
}else{
write(c_fd,writebuf,strlen(writebuf));
memset(readbuf,0,sizeof(readbuf));
fread(readbuf,1,sizeof(readbuf),hafile);
sleep(1);
write(c_fd,readbuf,strlen(readbuf));
printf("上传文件成功。\n");
fclose(hafile);
memset(readbuf,0,sizeof(readbuf));
}
return 0;
}
//从服务端下载文件的功能函数
int downloadFunction(int c_fd)
{
FILE *dwfile;
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
char *file=readbuf+strlen("download ");
printf("我要下载:%s\n",file);
dwfile=fopen(file,"w+");
if(dwfile==NULL)
{
printf("下载%s失败!\n",file);
return -1;
}else{
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
fwrite(readbuf,1,strlen(readbuf),dwfile);
fclose(dwfile);
printf("下载文件成功。\n");
}
return 0;
}
//执行客户端下可执行指令或程序并输出结果
int client_order(int c_fd)
{
FILE *fp;
fp=popen(writebuf,"r");
if(fp==NULL)
{
printf("客户端执行指令失败!\n");
return -1;
}
memset(readbuf,0,sizeof(readbuf));
fread(readbuf,1,sizeof(readbuf),fp);
printf("&&&&&&&&&&&&&&&&&&&< 客户.端 >&&&&&&&&&&&&&&&&&&&\n");
printf("%s",readbuf);
printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n");
fclose(fp);
return 0;
}
//该函数过滤不能执行的指令
int readInformation(int c_fd)
{
int order=1;
if(!strcmp(writebuf,"top"))order=FORBID;
else if(!strcmp(writebuf,"grep"))order=FORBID;
else if(!strcmp(writebuf,"sl"))order=FORBID;
else if(!strcmp(writebuf,"LS"))order=FORBID;
else if(!strcmp(writebuf,"dd"))order=FORBID;
else if(!strcmp(writebuf,"cat"))order=FORBID;
else if(!strcmp(writebuf,"lp"))order=FORBID;
else if(!strcmp(writebuf,"crontab"))order=FORBID;
else if(!strcmp(writebuf,"telnet"))order=FORBID;
else if(!strcmp(writebuf,"ftp"))order=FORBID;
else if(!strcmp(writebuf,"nslookup"))order=FORBID;
else if(!strcmp(writebuf,"passwd"))order=FORBID;
else if(!strcmp(writebuf,"su"))order=FORBID;
else if(strstr(writebuf,"ping"))order=FORBID;
if(strstr(writebuf,"cd "))
{
if(chdir(writebuf+3)==-1)
{
perror("cd");
return -1;
}
return 0;
}
if(order)
{
client_order(c_fd);
return 0;
}
printf("客户端下没有或不支持该指令!\n");
return -1;
}
int main(int argc,char **argv)
{
//首先判断入口参数是否正确,不正确则直接退出。
if(argc != 3)
{
perror("参数不正确。\n");
exit(-1);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>
printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n");
printf("& FTP System &\n");
printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n");
printf("提示:上传指令upload file,下载指令download file.\n");
printf("其余指令参考Liunx下的shell指令。\n");
printf("(切换客户端指令->CLIENT)服务端命令发送:\n");
//定义套接字和bind配置需要的结构体
int c_fd;
struct sockaddr_in c_addr;
int i;
//1.socket创建套接字
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
printf("创建套接字失败!\n");
perror("原因:");
exit(-1);
}else{
printf("服务端(server)IP:%s Prot:%s\n",argv[1],argv[2]);
}
//2.客户端的配置和connect连接服务端
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in))==-1)
{
printf("连接服务端失败!\n");
perror("原因:");
exit(-1);
}else{
printf("连接服务端成功。\n");
}
//3.read write与客户端的数据交互
//char readbuf[RL]; 接受
//char writebuf[64];发送
if(fork()==0)
{
while(1)
{
memset(writebuf,0,sizeof(writebuf));
gets(writebuf);
if(!strcmp(writebuf,"CLIENT"))
{
printf("提示:其余指令与服务端无异。\n");
while(1)
{
i=1;
printf("进入客户端(退出指令->QUIT):");
memset(writebuf,0,sizeof(writebuf));
gets(writebuf);
if(strstr(writebuf,"upload "))
{
i=0;
uploadFunction(c_fd);
}
if(!strcmp(writebuf,"QUIT"))break;
if(i)readInformation(c_fd);
}
printf("退出客户端命令页,进入服务端命令页。\n");
printf("(切换客户端指令->CLIENT)服务端命令发送:");
continue;
}
if(strstr(writebuf,"upload "))
{
printf("该命令在客户端页使用。\n");
continue;
}
write(c_fd,writebuf,sizeof(writebuf));
if(!strcmp(writebuf,"quit"))
{
printf("退出客户端。\n");
exit(0);
}
}
}
else{
while(1)
{
i=1;
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
if(!strcmp(readbuf,"quit"))exit(0);
if(strstr(readbuf,"nofile"))
{
i=0;
printf("没有这个文件!\n");
}
if(!strcmp(readbuf,"hafile"))
{
i=0;
downloadFunction(c_fd);
}
if(i)puts(readbuf);
}
}
return 0;
}
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define RL 10240
#define HAVEORDER1 1
#define HAVEORDER2 2
#define HAVEORDER3 3
#define CDORDER4 4
char readbuf[64]={0};
char writebuf[RL]={0};
//实现服务端文件下载到客户端的功能函数
int downloadFunction1(int c_fd)
{
FILE *dwfile;
write(c_fd,readbuf,strlen(readbuf));
char *file=readbuf+strlen("download ");
printf("下载文件:%s\n",file);
sleep(1);
dwfile=fopen(file,"r");
if(dwfile==NULL)
{
printf("啊偶!下载失败。\n");
return -1;
}else{
memset(writebuf,0,sizeof(writebuf));
fread(writebuf,1,sizeof(writebuf),dwfile);
write(c_fd,writebuf,strlen(writebuf));
printf("下载成功。\n");
}
return 0;
}
int downloadFunction0(int c_fd)
{
FILE *dwfile;
char *nofile={"nofile"};
char *hafile={"hafile"};
char *file=readbuf+strlen("download ");
printf("下载文件:%s\n",file);
dwfile=fopen(file,"r");
if(dwfile==NULL)
{
write(c_fd,nofile,strlen(nofile));
printf("下载文件失败!\n");
return 0;
}
write(c_fd,hafile,strlen(hafile));
sleep(1);
fclose(dwfile);
return 1;
}
//实现客户端文件上传到服务端的功能函数
int uploadFunction(int c_fd)
{
FILE *upfile;
char *file=readbuf+strlen("upload ");
printf("上传文件:%s\n",file);
upfile=fopen(file,"w+");
if(upfile==NULL)
{
printf("上传%s失败!\n",file);
return -1;
}else{
printf("上传文件成功。\n");
memset(writebuf,0,sizeof(writebuf));
read(c_fd,writebuf,sizeof(writebuf));
fwrite(writebuf,1,strlen(writebuf),upfile);
fclose(upfile);
memset(writebuf,0,sizeof(writebuf));
}
return 0;
}
//发送简单指令的执行信息
int orderFunction(int c_fd)
{
FILE *fp;
char *client_Input_hint={"\n(切换客户端指令->CLIENT)服务端命令发送:"};
char *first={"收到信息:"};
fp=popen(readbuf,"r");
if(fp==NULL)
{
write(c_fd,"服务器执行指令失败!",10);
return -1;
}
char *decorate0={"\n<<><><><><><><><><>< 服务.端 ><><><><><><><><><>>\n"};
char *decorate1={"<><><><><><><><><><><><>.<><><><><><><><><><><><>"};
memset(writebuf,0,sizeof(writebuf));
fread(writebuf,1,sizeof(writebuf),fp);
write(c_fd,first,strlen(first));
write(c_fd,decorate0,strlen(decorate0));
write(c_fd,writebuf,strlen(writebuf));
write(c_fd,decorate1,strlen(decorate1));
write(c_fd,client_Input_hint,strlen(client_Input_hint));
fclose(fp);
return 0;
}
//判断服务端中是否有客户端输入的指令
int readInformation()
{
int order=0;
if(!strcmp(readbuf,"ls"))order=HAVEORDER1;
else if(!strcmp(readbuf,"pwd"))order=HAVEORDER1;
else if(!strcmp(readbuf,"ps"))order=HAVEORDER1;
else if(!strcmp(readbuf,"ls -l"))order=HAVEORDER1;
else if(!strcmp(readbuf,"date"))order=HAVEORDER1;
else if(!strcmp(readbuf,"ifconfig"))order=HAVEORDER1;
else if(!strcmp(readbuf,"ls -al"))order=HAVEORDER1;
else if(!strcmp(readbuf,"df"))order=HAVEORDER1;
else if(strstr(readbuf,"cat "))order=HAVEORDER1;
else if(strstr(readbuf,"whereis"))order=HAVEORDER1;
else if(strstr(readbuf,"find"))order=HAVEORDER1;
else if(strstr(readbuf,"cd"))order=CDORDER4;
else if(strstr(readbuf,"download "))order=HAVEORDER2;
else if(strstr(readbuf,"upload "))order=HAVEORDER3;
return order;
}
int cd_order()
{
if(chdir(readbuf+3)==-1)
{
perror("cd");
return -1;
}
return 0;
}
//判断客户端发送过来的指令
int writeInformation(int c_fd)
{
char *have_Not_order={"抱歉没有或不支持这个指令,请重新输入!"};
int ordr=0;
ordr=readInformation();
switch(ordr)
{
case HAVEORDER1:
orderFunction(c_fd);
break;
case HAVEORDER2:
if(downloadFunction0(c_fd))
downloadFunction1(c_fd);
break;
case HAVEORDER3:
uploadFunction(c_fd);
break;
case CDORDER4:
cd_order();
break;
default:
write(c_fd,have_Not_order,strlen(have_Not_order));
}
return 0;
}
int main(int argc,char **argv)
{
//首先判断参数,不对直接退出。
if(argc != 3)
{
perror("参数不正确。\n");
exit(-1);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
printf("@ FTP system @\n");
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
//定义套接字和bind配置需要的结构体
int s_fd;
int c_fd;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
//1.socket创建套接字
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
printf("创建套接字失败!\n");
perror("原因:");
exit(-1);
}else{
printf("服务器(Server) IP:%s Prot:%s\n",argv[1],argv[2]);
}
//2.bind绑定IP地址和端口号,配置网络协议
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen监听客户端连接请求(三次握手)
listen(s_fd,7);
//4.accept连接完成三次握手的客户端
int clen=sizeof(struct sockaddr_in);
while(1)
{
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1)
{
printf("客户端连接进入失败!\n");
exit(-1);
}else{
printf("客户端连接进入...\n");
printf("客户端IP:%s\n",inet_ntoa(c_addr.sin_addr));
printf("客户端连接成功。\n");
}
//5.read write与客户端的数据交互
//char readbuf[64]; 接受指令
//char writebuf[RL];发送信息
if(fork()==0)
{
while(1)
{
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
printf("收到指令:%s\n",readbuf);
if(!strcmp(readbuf,"quit")){
printf("(客户端IP:%s)退出!\n",inet_ntoa(c_addr.sin_addr));
write(c_fd,"quit",4);
exit(0);
}
writeInformation(c_fd);
}
}
}
return 0;
}