一、一个简单的服务器和客户对接之后发送“hello world”的程序。
服务器程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<errno.h>
#include<netinet/in.h>
#include<sys/wait.h>
#define LISTENQ 5000
int main(int argc,char *argv[])
{
int sockfd,servfd,cliefd;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket error");
return(-1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8000);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
printf("bind error");
exit(0);
}
listen(sockfd,LISTENQ);
while(1)
{
printf("waitting fo connect!!\n");
if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))!=-1)
{
printf("连接成功!!\n");
send(cliefd,"hello world!",12,0);
}
else
{
printf("连接失败!!\n");
}
close(cliefd);
}
close(sockfd);
return 0;
}
客户程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<errno.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include"apue.h"
#define MAX 1500
int main(int argc,char *argv[])
{
int sockfd,cliefd;
struct sockaddr_in servaddr;
char buf[MAXLINE];
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("socket error");
return(-1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5000);
if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)//关键部分
err_quit("inet_pton error for%s,argv[1]");
if(connect(sockfd,(struct sockaddr*)&cliaddr,sizeof(struct sockaddr))==-1)
{
printf("连接失败!!!\n");
}
else
{
printf("连接成功!!!\n");
recv(sockfd,buf,MAX,0);
printf("%s",buf);
}
close(sockfd);
return 0;
}
编译过程中出现的问题以及原因:
1.服务器的 printf ("连接成功!!")无法输出
原因:在linux系统下,printf函数是行缓冲式的输出,当printf遇到\n时,或者缓冲区满时,才会将缓冲区里的内容刷新到标准输出(stdout).因此, printf("连接成功!!"); 等语句的显示不能立刻显示在屏幕上,但是printf("连接成功!!\n"); 可以
2.客户中的struct sockaddr_in对应的地址是哪个?
是服务器的地址描述符(servaddr)
3.bzero后面的参数写什么?
都是对于servaddr的处理
4.最大监听数要自己定义?
对于undp上的代码,最大监听数包含在头文件#include “und.p”,但是在我们自己写的代码要交代清楚
5. if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))!=-1)
accept后面的参数是指客户的一些消息,如果不要就直接写NULL。
6.关于send()和recv()
注意:recv函数仅仅是copy数据,真正的接收数据是协议来完成的。recv函数返回其实际copy的字节数
7.服务器中要写上close(sockfd);语句
因为写上close,在调用完之后就会关闭相关联系
8.inet_pton
9.程序中可能会运行不了,是因为会遇到不同的端口号,把端口号改成合适的就可以运行了
二、一个从服务器获得要的图片/文件的程序
服务器程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<errno.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#define BUFFER_SIZE 1024
#define FILE_NAME_SIZE 500
#define LISTENQ 5000
int main(int argc,char *argv[])
{
int sockfd,servfd,cliefd;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket error");
return(-1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8080);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
printf("bind error");
exit(0);
}
listen(sockfd,LISTENQ);
while(1)
{
printf("waitting fo connect!!\n");
if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))==-1)
{ printf("连接失败\n"); }else
{ printf("连接成功\n"); }
//记录文件内容
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
//把cliefd对应的客户的具体内容接收到buffer中,等待协议接收数据中断,返回-1
//实际上,recv只是把协议传输过来的数据进行复制而已
int length=recv(cliefd,buffer,BUFFER_SIZE,0);
if(length<0)
{
printf("文件获取失败\n");
break;
}
//记录文件的名字
char file_name[FILE_NAME_SIZE+1];
bzero(file_name,FILE_NAME_SIZE+1);
//strncpy是c语言一个复制字符串的函数
//函数原型strncpy(char *dest, const char *src, int n),把src的前n个字符复制到dest中
strncpy(file_name,buffer,strlen(buffer)>FILE_NAME_SIZE?FILE_NAME_SIZE:strlen(buffer));
//文件指针记录文件的打开的情况(rb是指以二进制的形式打开文件。这样就可以打开文档和图片等类型的文件)
FILE *fpp=fopen(file_name,"rb");
if(NULL==fpp)
{
printf("File %s Not Found\n",file_name);
}
else
{
bzero(buffer,BUFFER_SIZE);
int file_block_length=0;
while((file_block_length=fread(buffer,sizeof(char),BUFFER_SIZE,fpp))>0)
{
printf("file_block_length=%d\n",file_block_length);
if(send(cliefd,buffer,file_block_length,0)<0)
{
printf("Send File %s faild",file_name);
break;
}
bzero(buffer,BUFFER_SIZE);
}
//close(fp);
fclose(fpp);
printf("File %s transfer finished\n",file_name);
}
//关闭与客户端的连接
close(cliefd);
}
//关闭监听
close(sockfd);
return 0;
}
客户程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<errno.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include"apue.h"
#define MAX 1500
#define FILE_NAME_SIZE 512
#define BUFFER_SIZE 1024
int main(int argc,char *argv[])
{
int sockfd,cliefd;
struct sockaddr_in servaddr;
char buf[MAXLINE];
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("socket error");
return(-1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8080);
if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)//关键部分
err_quit("inet_pton error for%s,argv[1]");
if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))==-1)
{printf("连接失败!!!\n");}else
{printf("连接成功!!!\n");}
char file_name[FILE_NAME_SIZE+1];
bzero(file_name,FILE_NAME_SIZE+1);
printf("Please input the name of the file:");
scanf("%s",file_name);
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
strncpy(buffer,file_name,strlen(file_name)>FILE_NAME_SIZE?FILE_NAME_SIZE:strlen(file_name));
send(sockfd,buffer,BUFFER_SIZE,0);
FILE *fp=fopen(file_name,"wb");
if(NULL==fp)
{
printf("file %s can not to write\n",file_name);
exit(1);
}
//从服务器接收数据到buffer中
bzero(buffer,BUFFER_SIZE);
int length=0;
while(length=recv(sockfd,buffer,BUFFER_SIZE,0))
{
if(length<0)
{
printf("reveive data from server %s fail\n",file_name);
break;
}
int write_length=fwrite(buffer,sizeof(char),length,fp);
if(write_length<length)
{
printf("file %s write faild\n",file_name);
break;
}
bzero(buffer,BUFFER_SIZE);
}
printf("receive file %s from server finish\n",file_name);
fclose(fp);
close(sockfd);
return 0;
}
编译过程中出现的问题以及原因:
1.完成该过程的思路是什么?
当连接阶段完成之后,我们就可以互相传输文件。文件包括文件名和文件内容,这两个变量我们都要交代清楚。文件传输大致是这样的:现在两边都声明文件名和存放文件的缓存区名,然后把相应的存储空间清零。我们在客户端写入我们想要的文件名,名字放在缓存区中,发送给服务器。服务器从缓冲区中接收到信息,通过recv复制到服务器中,服务器通过fopen以二进制的形式查找到相应的内容,把内容通过send放在缓冲区中。客户端从缓冲区中读取到内容,通过recv复制到我们声明好的文件变量对应的文件,然后用fwrite打开并写入其中。
特别有一点要注意的,recv()和send()这两个函数可以理解为连接服务器、客户端以及缓冲区,它们的功能和copy差不多,实际上数据的复制是在协议中进行的。
2.函数strncpy(char *dest, const char *src, int n)
strncpy是c语言复制字符的函数。把src的前n个字符复制到dest中
3.fopen()、fwrite()、fread()
4.ping
ping是window、linux中一个命令。
ping也属于一个通信协议,是TCP/IP协议的一部分
利用ping命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障
应用格式:Ping空格 IP地址。
三、一个服务器和客户端通信的程序
服务器程序:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/wait.h>
#define MAX 5000
#define LISTENQ 1024
int main(int argc,char *argv[])
{
int sockfd,cliefd;
struct sockaddr_in servaddr,cliaddr;
char cli[MAX],ser[MAX];
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("socket error\n");
exit(0);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8080);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
int opt = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)
{
perror("Server setsockopt failed");
return 1;
}
if((bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
{
printf("bind error:%s\n",strerror(errno));
exit(0);
}
listen(sockfd,LISTENQ);
while(1)
{
printf("waiting for connecting.\n");
if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))<0)
{
printf("connect faild\n");
exit(1);
}
else
{
printf("connect success\n");
}
printf("welcome to chatting rome!!\n");
while(1)
{
recv(cliefd,cli,500,0);
if(cli[0]=='#')
{
printf("From client: %s\n",cli);
printf("Over\n");
exit(0);
}
else
{
printf("From client: %s\n",cli);
// bzero(&cli,sizeof(cli));
printf("To client:");
scanf("%s",ser);
send(cliefd,ser,500,0);
if(ser[0]=='#')
{
printf("Over\n");
bzero(ser,0);
bzero(&cliefd,sizeof(cliefd));
return(-1);
}
}
}
close(cliefd);
}
close(sockfd);
return 0;
}
客户端程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<netinet/in.h>
#include<errno.h>
#define MAX 1500
int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
char ser[MAX],cli[MAX];
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("soket error\n");
exit(0);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8080);
inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))<0)
{
printf("connect error:%s\n",errno);
exit(0);
}
else
{
printf("connect success\n");
printf("welcome to chatting rome!\n");
}
while(1)
{
printf("To server:");
scanf("%s",cli);
send(sockfd,cli,500,0);
if(cli[0]=='#')
{
printf("Over\n");
exit(0);
}
else
{
bzero(&cli,sizeof(cli));
recv(sockfd,ser,500,0);
printf("From server: %s\n",ser);
if(ser[0]=='#')
{
printf("Over\n");
exit(0);
}
bzero(&ser,sizeof(ser));
}
}
close(sockfd);
return 0;
}
编译过程中出现的问题以及原因:
1.server完成一次对接之后(在server结束),再次执行程序,出现address already in use这样的错误信息,表示我端口已经被占用
(参考资料https://www.cnblogs.com/CodeMIRACLE/p/5122063.html)
只要在bind函数之前加入下面代码即可:
int opt = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)
{
perror("Server setsockopt failed");
return 1;
}
bind普遍遭遇的问题是试图绑定一个已经在使用的端口,该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口,它是由TCP套接字状态TIME_WAIT引起的。在TIME_WAIT状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。
2.怎么查看程序运行失败是出现什么问题?
因为我们在头文件里有交代#include<errno.h>,所以我们是可以通过调用strerror(errno)来查看到底是什么错误
3.通配符地址(INADDR_ANY)
INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或"所有地址"、"任意地址"。 一般来说,在各个系统中均定义成为0值