Unix网络编程---第二次作业
要求:
客户端:
1、从命令行读入服务器的IP地址;并连接到服务器;
2、循环从命令行读入一行字符串,并传递给服务器,由服务器对字符串反转并将结果返回客户程序;
3、客户程序显示反转后的字符串;
服务器端:
1、接收客户的连接请求,并显示客户的IP地址和端口号;
2、接收客户传来的字符串,反转后传递给客户;
程序实现:
服务器端:my_server2.c
#include <sys/socket.h>
#include <sys/types.h>/*The funcion sizeof,socklen_t need*/
#include <netinet/in.h>/*The funcion sockaddr_in need*/
#include <unistd.h>
#include <arpa/inet.h>/*The funcion inet_ntoa need*/
#include <string.h>/*The funcion strlen need*/
#include <errno.h>/*errno == EINTR*/
#include <sys/wait.h>/*WNOHANG*/
#define UPORT 8088 /*This is the port number used by me */
#define MAXLINE 255
#define LISTENQ 32
void str_echo(int sockfd) {
ssize_t n;
char buf[MAXLINE+1], tmp;
int i, j;
again:
while ( (n=read(sockfd, buf, MAXLINE)) > 0) { /*blocking*/
printf("client input string:%s",buf);
for(i=0, j=n-3; i<j; i++, j--) {
tmp=buf[i];
buf[i]=buf[j];
buf[j]=tmp;
/*printf("%d ",i); */
}
/*printf("strlen:%d ",strlen(buf)); */
/*printf("i:j,%d:%d ",i,j); */
if(write(sockfd, buf, n)==-1) {
perror("write error");
exit(-1);
}
printf("inverted order string:%s",buf);
//printf("line break:%c",' ');/*the printf is line buffer*/
//printf("line break:%s"," ");
//printf("n:%d ", n);
}
if (n<0 && errno == EINTR) {
goto again;
}
else if (n<0) {
perror("str_echo:read error");
exit(-1);
}
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
while( (pid = waitpid(-1,&stat,WNOHANG))>0)
printf("child %d terminated ", pid);
return;
}
int main(int argc, char **argv)
{
int listenfd, connfd,reuse=1; /* if the value of reuse is not zero, mean open this reuse address selection, or else ban this function*/
struct sockaddr_in servaddr, cliaddr;
socklen_t clilen;
pid_t childpid;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(UPORT); /* daytime server */
if( setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){
perror("There is an error occured when the program set REUSEADDR symbol");
return -1;
}
if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))==-1){
perror("%s ","bind error");
exit(-1);
}
listen(listenfd, LISTENQ);
signal(SIGCHLD, sig_chld);
for ( ; ; ) {
clilen=sizeof(cliaddr);/*this line should in the for,because everytime ciculation the value of the clilen will be changed by the following lines program,so we should reset it */
if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen))==-1){ /*blocking*/
perror("%s ","An error occured while tring to creat a connfd! ");
exit(-1);
}
printf("the new connection address is:%s:%d ",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
if( (childpid=fork())==0) { /* child process*/
close(listenfd);/*close listening socket*/
str_echo(connfd);/*process the request*/
exit(0);
}
close(connfd); /*parent closes connected socket*/
}
}
客户端:my_client2.c
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define UPORT 8088 /*This is the port number used by me */
#define MAXLINE 255
void str_cli(FILE *fp, int sockfd) {
char sendline[MAXLINE+1], recvline[MAXLINE+1];
while (fgets(sendline, MAXLINE+1, fp) != NULL) { /*read at most MAXLINE character, auto add 0 in the end,blocking until end a time of stdin by clicking the Enter. when the length of input string less than the MAXLINE the fgets can read-in the line break character ' '*/
if(write(sockfd, sendline, (strlen(sendline)+1)) == -1) { /*the write do not stop in the line break and ' ' ,it will stop writing when the number of writing character equal it, note that the strlen did not inlcude the ' ' character,so we make the strlen add one,eg:str="123 ";strlen(str) is 7,actually it contains 8 character, the last char is ' '. */
perror("write error");
exit(-1);
}
if(read(sockfd, recvline, MAXLINE) <= 0 ) { /*read will end in encounter the EOF or number of character less than MAXLINE or equal, rather than the line break or ' '*/
//perror("server terminated prematurely!");/*when use this function, if no error will output :Success*/
printf("server terminated prematurely! ");
exit(0);
}
//recvline(MAXLINE)=0;/*auto set 0 by initializing*/
fputs(recvline,stdout);
}
}
int main(int argc, char **argv)
{
int sockfd, n;
struct sockaddr_in servaddr;
if (argc != 2){
perror("usage: a.out <IPaddress>");
exit(-1);
}
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket error");
exit(-1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(UPORT); /* daytime server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s", argv[1]);
exit(-1);
}
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
perror("connect error");
exit(-1);
}
str_cli(stdin,sockfd); /*do it all*/
exit(0);
}
运行截图:
编译:gcc my_client.c -o my_client
gcc my_server.c -o my_server
服务端运行:./my_server
客户端运行:./my_client 192.168.1.119
本实验是在两台虚拟机上操作,服务端ip为192.168.1.119,客户端ip为192.168.1.120
正常结束
1、 客户端:正常结束EOF(ctrl+D),fges=null
2、 服务端:收到EOF,子进程终止,父进程收到终止信号并处理,打印出子进程pid,释放子进程所占用内存,避免僵尸进程。
服务器进程先终止
1、通过设置signal,捕获SIGHLD子进程终止的信息,作相应的处理输出。
2、当子进程被kill子后会向客户端发送FIN,客户端收到之后回复ACK,关闭已经连接的套接字。
3、关闭连接之后,当再次向服务端发送字符时,服务端返回reset,于是客户端进程退出。
4、如果客户端接收到reset的之后再发送字符,那么这时候内核就会向该进程发送一个SIGPIPE信号,终止进程。
本程序涉及到前3点,不会出现第4点情况,因为当读到的字符为null时,客户进程将打印出信息,并推出。注意当没有输入任何字符按回车之后,fgets将读到换行符 ,只有客户端收到EOF时,表示文件结束符,此时fgets不会读到任何字符,即为null。
1、 服务端:查找子进程pid并kill
2、客户端:终止之后再发送字符,返回服务端提前终止信息
总结:
1、 fgets若从标准输入stdin读字符串,当所给的字符串长度小于MAXLINE-1时,会读入换行符并在最后加上 ,否则,读入MAXLINE-1个字符加 ;
2、 read,write写文件以EOF为结束,写字符串别忘了写上
3、 strlen不包括 长度,所以在write时要strlen+1