父进程负责与客户端建立链接,子进程负责通信
//TCPserve.c
#include <stdio.h>
#include <unistd.h> //linux标准头文件,包含了各种linux系统服务函数原型和数据结构的定义
#include <sys/types.h> //基本数据类型头文件,包含有基本系统数据类型定义
#include <sys/socket.h> //提供套接字函数原型与数据结构的定义
#include <netinet/in.h> //提供数据结构sockaddr_in的定义
#include <arpa/inet.h> //提供ip地址转函数原型的定义
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#define PORT 1234 //定义服务器端端口
#define QLEN 10 //定义允许排队的连接数
#define BUFSIZE 1024 //定义缓冲区大小为1024b
void sig_chld(int); //声明sig_chld()函数
void process_cli(int connectfd,struct sockaddr_in client); //声明用于处理与客户之间通信的子函数
int main()
{
int listenfd,connectfd; //定义主套接字和从套接字
pid_t pid; //定义进程标识变量
struct sockaddr_in server; //定义服务器端点地址结构体变量
struct sockaddr_in client; //定义客户端端点地址结构体变量
socklen_t ssize;
if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1) //创建主套接字
{
perror("Creating socket failed.");
exit(1);
}
int opt = SO_REUSEADDR; //设置与主套接字关联的选项,允许主套接字重用本地地址和端口
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bzero(&server,sizeof(server)); //清空服务器端点地址结构体变量
//以下代码段用于对服务器端点地址结构体变量进行赋值
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
//调用bind()函数将主套接字绑定到服务器的端点地址
if(bind(listenfd,(struct sockaddr*)&server,sizeof(struct sockaddr)) == -1)
{
perror("bind error\n");
exit(1); //调用bind()函数出错则退出系统
}
if(listen(listenfd,QLEN) == -1) //调用listen()函数使主套接字处于被动监听模式,并为主套接字建立一个数据输入队列
{
perror("listen() error\n");
exit(1); //调用listen()出错则退出系统
}
ssize = sizeof(struct sockaddr_in);
signal(SIGCHLD,sig_chld); //调用signal()函数为SIGCHLD信号安装handler
while(1) //循环调用accpet()函数接受客户连接请求,建立连接,并创建新的从套接字connectfd用于处理该连接
{
if((connectfd = accept(listenfd,(struct sockaddr*)&client,&ssize)) == -1)
{
perror("accept()error\n");
exit(1);
}
if((pid = fork()) > 0) //调用fork()函数创建新的从进程
{
close(connectfd); //在父进程中关闭从淘金诶子描述符
continue; //父进程返回while循环
}
else if(pid == 0)
{
close(listenfd); //在子进程中关闭主套接字描述符
process_cli(connectfd,client); //在子进程中调用process_cli()函数基于新建的从套接字connectfd来处理与客户之间的通信
exit(0); //处理完毕与客户的通信之后退出子进程
}
else
{
//调用fork()创建从进程出错退出系统
printf("fork error\n");
exit(0);
}
}
close(listenfd); //主进程结束时关闭主套接字
return 0;
}
void process_cli(int connectfd,struct sockaddr_in client)
{
int num;
char recvbuf[BUFSIZE],sendbuf[BUFSIZE],cli_name[BUFSIZE]; //定义相关缓存区
int len;
printf("You got a connection from %s.",inet_ntoa(client.sin_addr)); //打印输出客户的ip地址
num = recv(connectfd,cli_name,BUFSIZE,0); //从从套接字中读取客户发送过来的数据(即接收该客户的名字)
if(num == 0) //若从套接字中数据读取完毕则关闭该套接字并返回
{
close(connectfd);
printf("Client disconnected.\n");
return ;
}
cli_name[num-1] = '\0'; //在字符串末尾添加字符串结束符
printf("Client's name is %s.\n",cli_name); //打印输出客户的名字
while(num = recv(connectfd,recvbuf,BUFSIZE,0)) //循环接收来自客户的其他信息
{
recvbuf[num] = '\0'; //在字符串末尾添加字符串结束符
printf("Recived client(%s)message:%s",cli_name,recvbuf);
for(int i = 0;i<num-1;i++) //将接收到的客户消息进行反转
{
sendbuf[i] = recvbuf[num-i -2];
}
sendbuf[num-1] = '\0'; //在字符串末尾添加字符串结束符
len = strlen(sendbuf);
send(connectfd,sendbuf,len,0); //将反转后的数据回送给客户端
}
close(connectfd); //关闭从套接字
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1,&stat,WNOHANG))>0)
{
/*
*
*调用waitpid()函数等待子进程结束,若
有子进程结束,则waitpid()函数将对其
回收,从而避免产生僵尸进程
* */
printf("chile%d terminated\n",pid);
}
return;
}
//TCPclient.c
#include <stdio.h>
#include <unistd.h> //LINUX标准头文件,包含了各种LINUX系统服务函数原型和数据结构的定义
#include <string.h>
#include <sys/types.h> //基本数据类型头文件,含有基本数据类型的定义
#include <sys/socket.h> //提供套接字函数原型的定义
#include <netinet/in.h> //提供数据结构sockaddr_in的定义
#include <netdb.h> //含有hostent结构与gethostbyname函数的定义
#include <stdlib.h>
#define PORT 1234 //定义端口号
#define BUFSIZE 100 //定义缓冲区大小
void process(FILE *fp,int sockfd); //声明用于处理与服务器之间的通信子函数
char *getMessage(char *sendline,int line,FILE *fp); //声明用于实现接受用户键盘输入数据的子函数
int main(int argc,char *argv[])
{
int fd; //定义文件描述符变量
struct hostent *he; //定义hostent结构变量
struct sockaddr_in server;
if(argc!=2) //若用户输入的命令行参数错误则提示用法并退出系统
{
printf("Usage:%s<IP Adress>\n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL) //调用gethostbyname()由用户输入的远程服务器的十进制ip地址获得其二进制的ip地址
{
printf("gethostbyname() error\n");
exit(1);
}
if((fd = socket(AF_INET,SOCK_STREAM,0)) == -1) //创建套接字
{
printf("socket() error\n");
exit(1);
}
bzero(&server,sizeof(server)); //清空服务器端点地址结构体变量
//以下代码段用于对服务器端点地址结构体变量进行赋值
server.sin_family = AF_INET;
server.sin_port=htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);
if(connect(fd,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) //调用connect()函数向远程服务器发出连接请求
{
printf("connect() error\n");
exit(1);
}
process(stdin,fd); //调用子process()函数基于新创建的套接字与服务器之间进行交互
close(fd); //交互完毕,关闭套接字
return 0;
}
void process(FILE *fp,int sockfd)
{
char sendline[BUFSIZE],recvline[BUFSIZE];
int numbytes;
printf("Connected to server.\n");
printf("Input name:");
if(fgets(sendline,BUFSIZE,fp) == NULL) //调用fgets()函数接受用户键盘输入的客户端名字并存入缓冲区sendline
{
printf("\nExit\n");
return;
}
send(sockfd,sendline,strlen(sendline),0); //将sendline中缓存的客户端名字发送给服务器
while(getMessage(sendline,BUFSIZE,fp)!=NULL) //循环调用getMessage()函数接受用户键盘输入的信息并存入缓冲区sendline
{
send(sockfd,sendline,strlen(sendline),0); //将缓存在sendline中的信息发送给服务器
if((numbytes = recv(sockfd,recvline,BUFSIZE,0)) == 0) //调用recv()函数接收服务器回送的信息并存入缓冲区recvline
{
printf("Server terminated.\n");
return;
}
recvline[numbytes] = '\0'; //在字符串末尾添加字符串结束符号
printf("Server Message:%s\n",recvline); //打印输出服务器的回送信息内容
}
printf("\nExit. \n");
}
char *getMessage(char *sendline,int len,FILE* fp)
{
printf("Input string to server:");
return(fgets(sendline,BUFSIZE,fp)); //调用fgets()函数接收用户键盘输入的信息并存入缓冲区sendline
}