#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int sockfd; //套接字描述符
void sig_handler(int signo){ //信号处理函数
if(signo==SIGINT){
printf("serve close\n");
/*步骤6:关闭socket
*这里关闭服务器socket
*/
close(sockfd);
exit(1);
}
}
void out_addr(struct sockaddr_in *clientaddr){ //输出连接上来的客户端相关信息
//将端口从网络字节序转化为主机字节序
int port=ntohs(clientaddr->sin_port);
char ip[16];
memset(ip,0,sizeof(ip));
//将ip地址从网络字节序转化为点分十进制
inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
printf("client: %s(%d) connected\n",ip,port);
}
void do_service(int fd){ //向客户端发送系统时间
//获得系统时间
long t=time(0); //获得系统时间
char *s=ctime(&t); //将系统时间转化为字符串
size_t size=strlen(s)*sizeof(char); //获得字符串大小
//将服务端获得的系统时间返回到客户端
if(write(fd,s,size)!=size){
perror("write errpr");
//exit(1);
}
}
int main(int argc,char* argv[]){
if(argc<2){ //如果没有输入端口号
printf("usage: %s #port\n",argv[0]);
exit(1);
}
if(signal(SIGINT,sig_handler)==SIG_ERR){
printf("signal sigint error");
exit(1);
}
/*步骤1:创建socket(套接字)
*注:socket创建在内核中,是一个结构体。
AF_INET:IPv4因特域
SOCK_STREAM:流式套接字,TCP协议
*/
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
/*步骤2:调用bind函数将socket和地址
*(包括ip、port)进行绑定
*/
struct sockaddr_in serveraddr; //因特网专用地址
memset(&serveraddr,0,sizeof(serveraddr)); //初始化一下
//往地址中填入ip、port、internet地址族类型
serveraddr.sin_family=AF_INET; //IPv4
serveraddr.sin_port=htons(atoi(argv[1])); //端口,由命令行传入,需要用htons函数转成网络字节序
//serveraddr.sin_addr.s_addr="192.168.0.10"; //ip地址这样设置的话,ip地址就绑死了
serveraddr.sin_addr.s_addr=INADDR_ANY; //监听服务器上所有网卡的ip
if(bind(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){ //将套接字与地址绑定,地址需要强转成通用地址形式
perror("bind error");
exit(1);
}
/*步骤3:调用listen函数启用监听(指定port监听)
*通知系统去接管来自客户端的连接请求
*将接受到的客户端的连接请求放在对应的队列中
*listen的第二个参数是制定这个队列的大小
*/
if(listen(sockfd,10)<0){
perror("listen error");
exit(1);
}
/*步骤4:调用acept函数从队列中获得一个客户端的请求连接
*并返回一个新的客户端描述符
*若没有客户端连接,则会阻塞,知道有一个客户端连接
*/
struct sockaddr_in clientaddr; //用于保存客户端地址
socklen_t clientaddr_len=sizeof(clientaddr); //客户端地址长度
while(1){
int fd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
if(fd<0){
perror("accept error");
continue;
}
/*步骤5:调用IO函数(read/write)和连接的客户端
*进行双向通讯
*/
out_addr(&clientaddr);
do_service(fd);
/*步骤6:关闭socket
*这里关闭客户端socket
*/
close(fd);
}
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
if(argc<3){
printf("usage: %s ip port\n",argv[0]);
exit(1);
}
/*步骤1:创建socket
*/
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
//往serveraddr填入ip、port、地址族类型(IPv4)
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
//将ip地址转化为网络字节序
inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
/*步骤2:客户端调用connect函数连接到服务器端
*/
if(connect(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){
perror("connect error");
exit(1);
}
/*步骤3:调用IO函数(read/write)和服务器
*进行双向通信
*/
char buffer[1024];
memset(buffer,0,sizeof(buffer));
size_t size;
if((size=read(sockfd,buffer,sizeof(buffer)))<0){ //读取服务器传过来的信息
perror("read error");
exit(1);
}
if(write(STDOUT_FILENO,buffer,size)!=size){ //将信息输出到屏幕上
perror("writer error");
}
/*步骤4:关闭socket套接字
*/
close(sockfd);
return 0;
}
#ifndef __MSG_H__
#define __MSG_H__
#include
typedef struct{
//协议头部
char head[10];
char checknum; //校验码
//协议体部
char buff[512]; //数据
}Msg;
/*
*发送一个基于自定义协议的message
*发送的数据存放在buff中
*/
extern int write_msg(int sockfd,char *buff,size_t len);
/*
*读取一个基于自定义协议的message
*读取的数据存放在buff中
*/
extern int read_msg(int sockfd,char *buff,size_t len);
#endif
#include "msg.h"
#include
#include
#include
#include
/*
*校验函数
*/
static unsigned char msg_check(Msg *pMessage) //只在本文件使用
{
unsigned char checknum=0;
for(int i=0;i<sizeof(pMessage->head);++i){
checknum+=pMessage->head[i];
}
for(int i=0;i<sizeof(pMessage->buff);++i){
checknum+=pMessage->buff[i];
}
return checknum;
}
/*
*发送一个基于自定义协议的message
*发送的数据存放在buff中
*/
int write_msg(int sockfd,char *buff,size_t len)
{
Msg message;
memset(&message,0,sizeof(message));
strcpy(message.head,"Night00:"); //头部
memcpy(message.buff,buff,len); //数据
message.checknum=msg_check(&message); //校验码
if(write(sockfd,&message,sizeof(message))!=sizeof(message))
return -1;
return 0;
}
/*
*读取一个基于自定义协议的message
*读取的数据存放在buff中
*/
int read_msg(int sockfd,char *buff,size_t len)
{
Msg message;
memset(&message,0,sizeof(message));
size_t size;
if((size=read(sockfd,&message,sizeof(message)))<0)
return -1;
else if(size==0)
return 0;
//进行校验码验证
unsigned char checknum=msg_check(&message);
if((checknum==(unsigned char)message.checknum) && (!strcmp("Night00:",message.head))){
memcpy(buff,message.buff,len);
return sizeof(message);
}
return -1;
}
#include"msg.h"
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
if(argc<3){
printf("usage: %s ip port\n",argv[0]);
exit(1);
}
/*步骤1:创建socket
*/
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
//往serveraddr填入ip、port、地址族类型(IPv4)
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
//将ip地址转化为网络字节序
inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
/*步骤2:客户端调用connect函数连接到服务器端
*/
if(connect(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){
perror("connect error");
exit(1);
}
/*步骤3:调用IO函数(read/write)和服务器
*进行双向通信
*/
char buff[512];
size_t size;
char *prompt=">";
while(1){
memset(buff,0,sizeof(buff));
write(STDOUT_FILENO,prompt,1); //打印到屏幕
size=read(STDIN_FILENO,buff,sizeof(buff)); //输入字符串,read并不会将最后一位设置为'\0'
char endChar[]="close socket"; //当输入close socket的时候退出循环
if(size==sizeof(endChar) && !strncmp(buff,endChar,strlen(endChar))){
break;
}
if(size<0)
continue;
buff[size-1]='\0'; //将数组最后一位作为结束符
if(write_msg(sockfd,buff,sizeof(buff))<0){
perror("write msg error"); //输入出错
continue;
}else{ //输入成功,接受服务器端数据
if(read_msg(sockfd,buff,sizeof(buff))<0){
perror("read msg error"); //读取出错
continue;
}else{
printf("%s\n",buff); //读取成功
}
}
}
/*步骤4:关闭socket套接字
*/
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"msg.h"
int sockfd; //套接字描述符
void sig_handler(int signo){ //信号处理函数
if(signo==SIGINT){
printf("serve close\n");
/*步骤6:关闭socket
*这里关闭服务器socket
*/
close(sockfd);
exit(1);
}
if(signo==SIGCHLD){
printf("child process deaded...");
wait(0);
}
}
void out_addr(struct sockaddr_in *clientaddr){ //输出连接上来的客户端相关信息
//将端口从网络字节序转化为主机字节序
int port=ntohs(clientaddr->sin_port);
char ip[16];
memset(ip,0,sizeof(ip));
//将ip地址从网络字节序转化为点分十进制
inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
printf("client: %s(%d) connected\n",ip,port);
}
void do_service(int fd){ //和客户端进行双向通信
/*和客户端进行读写操作(双向通信)*/
char buff[512];
while(1){
memset(buff,0,sizeof(buff));
printf("start read and write...\n");
size_t size;
if((size==read_msg(fd,buff,sizeof(buff)))<0){
perror("protocal error"); //如果数据读取错误,则跳出循环
break;
}else if(size==0){
/*可能有一种情况s,当客户端连接关闭
*即写端关闭,只留下读端,当缓冲区里
*的数据都被读完后,那么读取的大小就是0
*补充:若写端没有关闭,但是写端又不写入
*数据,那么读端就会阻塞。
*/
break;
}else{ //正常读取
if(write_msg(fd,buff,sizeof(buff))<0){
if(errno==EPIPE){
/*当服务端想向客户端写入数据时,若客户端断开连接
*就类似与管道的读端关闭,只留下写段,这样会产生
*一个SIGPIPE信号,同时errno会等于EPIPE
*/
break;
}
perror("protocal error");
}
printf("%s\n",buff);
}
}
}
int main(int argc,char* argv[]){
if(argc<2){ //如果没有输入端口号
printf("usage: %s #port\n",argv[0]);
exit(1);
}
if(signal(SIGINT,sig_handler)==SIG_ERR){
printf("signal sigint error");
exit(1);
}
if(signal(SIGCHLD,sig_handler)==SIG_ERR){
perror("signal sigchld error");
exit(1);
}
/*步骤1:创建socket(套接字)
*注:socket创建在内核中,是一个结构体。
AF_INET:IPv4因特域
SOCK_STREAM:流式套接字,TCP协议
*/
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
/*步骤2:调用bind函数将socket和地址
*(包括ip、port)进行绑定
*/
struct sockaddr_in serveraddr; //因特网专用地址
memset(&serveraddr,0,sizeof(serveraddr)); //初始化一下
//往地址中填入ip、port、internet地址族类型
serveraddr.sin_family=AF_INET; //IPv4
serveraddr.sin_port=htons(atoi(argv[1])); //端口,由命令行传入,需要用htons函数转成网络字节序
//serveraddr.sin_addr.s_addr="192.168.0.10"; //ip地址这样设置的话,ip地址就绑死了
serveraddr.sin_addr.s_addr=INADDR_ANY; //监听服务器上所有网卡的ip
if(bind(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){ //将套接字与地址绑定,地址需要强转成通用地址形式
perror("bind error");
exit(1);
}
/*步骤3:调用listen函数启用监听(指定port监听)
*通知系统去接管来自客户端的连接请求
*将接受到的客户端的连接请求放在对应的队列中
*listen的第二个参数是制定这个队列的大小
*/
if(listen(sockfd,10)<0){
perror("listen error");
exit(1);
}
/*步骤4:调用acept函数从队列中获得一个客户端的请求连接
*并返回一个新的客户端描述符
*若没有客户端连接,则会阻塞,知道有一个客户端连接
*/
struct sockaddr_in clientaddr; //用于保存客户端地址
socklen_t clientaddr_len=sizeof(clientaddr); //客户端地址长度
while(1){
int fd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
if(fd<0){
perror("accept error");
continue;
}
/*步骤5:fork一个子进程
*调用IO函数(read/write)和连接的客户端
*进行双向通讯
*/
pid_t pid=fork();
if(pid<0){
continue;
}else if(pid==0){ //子进程
out_addr(&clientaddr);
do_service(fd);
/*步骤6:关闭socket
*这里关闭客户端socket
*/
close(fd);
}else{ //父进程
/*当fork一个子进程的时候,子进程与父进程的环境相同
*因此,套接字的描述符fd也会复制一份,而内核对fd有
*一个类似于引用计数的东西,当父进程和子进程都close
*掉这个fd时,这个套接字才会真正关闭
*/
close(fd);
}
}
return 0;
}
(有问题,待更新)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"msg.h"
int sockfd; //套接字描述符
void sig_handler(int signo){ //信号处理函数
if(signo==SIGINT){
printf("serve close\n");
/*步骤6:关闭socket
*这里关闭服务器socket
*/
close(sockfd);
exit(1);
}
}
void out_addr(struct sockaddr_in *clientaddr){ //输出连接上来的客户端相关信息
//将端口从网络字节序转化为主机字节序
int port=ntohs(clientaddr->sin_port);
char ip[16];
memset(ip,0,sizeof(ip));
//将ip地址从网络字节序转化为点分十进制
inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
printf("client: %s(%d) connected\n",ip,port);
}
void do_service(int fd){ //和客户端进行双向通信
/*和客户端进行读写操作(双向通信)*/
char buff[512];
while(1){
memset(buff,0,sizeof(buff));
printf("start read and write...\n");
size_t size;
if((size==read_msg(fd,buff,sizeof(buff)))<0){
perror("protocal error"); //如果数据读取错误,则跳出循环
break;
}else if(size==0){
/*可能有一种情况s,当客户端连接关闭
*即写端关闭,只留下读端,当缓冲区里
*的数据都被读完后,那么读取的大小就是0
*补充:若写端没有关闭,但是写端又不写入
*数据,那么读端就会阻塞。
*/
break;
}else{ //正常读取
if(write_msg(fd,buff,sizeof(buff))<0){
if(errno==EPIPE){
/*当服务端想向客户端写入数据时,若客户端断开连接
*就类似与管道的读端关闭,只留下写段,这样会产生
*一个SIGPIPE信号,同时errno会等于EPIPE
*/
break;
}
perror("protocal error");
}
printf("%s\n",buff);
}
}
}
void out_fd(int fd){ //通过套接字输出客户端信息
struct sockaddr_in clientaddr;
socklen_t len=sizeof(clientaddr);
if(getpeername(fd,(struct sockaddr*)&clientaddr,&len)<0){
perror("getpeername error");
return;
}
//将端口从网络字节序转化为主机字节序
int port=ntohs(clientaddr.sin_port);
char ip[16];
memset(ip,0,sizeof(ip));
//将ip地址从网络字节序转化为点分十进制
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ip,sizeof(ip));
printf("client: %s(%d) closed\n",ip,port);
}
void* th_fn(void *arg){ //线程运行函数
int fd=*(int*)arg;
do_service(fd); //和客户端进行双向通信
out_fd(fd); //输出一个客户端相关信息
close(fd);
return (void*)0;
}
int main(int argc,char* argv[]){
if(argc<2){ //如果没有输入端口号
printf("usage: %s #port\n",argv[0]);
exit(1);
}
if(signal(SIGINT,sig_handler)==SIG_ERR){
printf("signal sigint error");
exit(1);
}
/*步骤1:创建socket(套接字)
*注:socket创建在内核中,是一个结构体。
AF_INET:IPv4因特域
SOCK_STREAM:流式套接字,TCP协议
*/
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
perror("socket error");
exit(1);
}
/*步骤2:调用bind函数将socket和地址
*(包括ip、port)进行绑定
*/
struct sockaddr_in serveraddr; //因特网专用地址
memset(&serveraddr,0,sizeof(serveraddr)); //初始化一下
//往地址中填入ip、port、internet地址族类型
serveraddr.sin_family=AF_INET; //IPv4
serveraddr.sin_port=htons(atoi(argv[1])); //端口,由命令行传入,需要用htons函数转成网络字节序
//serveraddr.sin_addr.s_addr="192.168.0.10"; //ip地址这样设置的话,ip地址就绑死了
serveraddr.sin_addr.s_addr=INADDR_ANY; //监听服务器上所有网卡的ip
if(bind(sockfd,(struct sockaddr*)&serveraddr,
sizeof(serveraddr))<0){ //将套接字与地址绑定,地址需要强转成通用地址形式
perror("bind error");
exit(1);
}
/*步骤3:调用listen函数启用监听(指定port监听)
*通知系统去接管来自客户端的连接请求
*将接受到的客户端的连接请求放在对应的队列中
*listen的第二个参数是制定这个队列的大小
*/
if(listen(sockfd,10)<0){
perror("listen error");
exit(1);
}
/*步骤4:调用acept函数从队列中获得一个客户端的请求连接
*并返回一个新的客户端描述符
*若没有客户端连接,则会阻塞,知道有一个客户端连接
*/
struct sockaddr_in clientaddr; //用于保存客户端地址
socklen_t clientaddr_len=sizeof(clientaddr); //客户端地址长度
//设置线程的分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
while(1){
//主控线程负责accept
/*这里的clientaddr可以获得客户端的地址
*但是也可以不这样做,可以从fd中获得客
*户端地址,使用getpeername函数
*/
int fd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
//int fd=accept(sockfd,NULL,NULL);
if(fd<0){
perror("accept error");
continue;
}
out_addr(&clientaddr);
/*步骤5:启动子线程(以分离状态启动,子线程运行结束
*资源自动回收,主线程不用阻塞来等待它回收资源)
*调用IO函数(read/write)和连接的客户端
*进行双向通讯
*/
pthread_t th;
int err;
if((err=pthread_create(&th,&attr,th_fn,(void *)&fd))!=0){
perror("pthread create error");
}
pthread_attr_destory(&attr);
}
return 0;
}