Unix網絡編程---第二次做業編程
要求:服務器
客戶端:網絡
一、從命令行讀入服務器的IP地址;並鏈接到服務器;less
二、循環從命令行讀入一行字符串,並傳遞給服務器,由服務器對字符串反轉並將結果返回客戶程序;socket
三、客戶程序顯示反轉後的字符串;this
服務器端:命令行
一、接收客戶的鏈接請求,並顯示客戶的IP地址和端口號;server
二、接收客戶傳來的字符串,反轉後傳遞給客戶;blog
程序實現:進程
服務器端: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\n",i); */
}
/*printf("strlen:%d\n",strlen(buf)); */
/*printf("i:j,%d:%d\n",i,j); */
if(write(sockfd, buf, n)==-1) {
perror("write error");
exit(-1);
}
printf("inverted order string:%s",buf);
//printf("line break:%c",'\n');/*the printf is line buffer*/
//printf("line break:%s","\n");
//printf("n:%d\n", 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\n", 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\r\n","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\r\n","An error occured while tring to creat a connfd! ");
exit(-1);
}
printf("the new connection address is:%s:%d\r\n",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 '\n'*/
if(write(sockfd, sendline, (strlen(sendline)+1)) == -1) { /*the write do not stop in the line break and '\0' ,it will stop writing when the number of writing character equal it, note that the strlen did not inlcude the '\0' character,so we make the strlen add one,eg:str="123\n";strlen(str) is 7,actually it contains 8 character, the last char is '\0'. */
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 '\0'*/
//perror("server terminated prematurely!");/*when use this function, if no error will output :Success*/
printf("server terminated prematurely!\n");
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
正常結束
一、 客戶端:正常結束EOF(ctrl+D),fges=null
二、 服務端:收到EOF,子進程終止,父進程收到終止信號並處理,打印出子進程pid,釋放子進程所佔用內存,避免殭屍進程。
服務器進程先終止
一、經過設置signal,捕獲SIGHLD子進程終止的信息,做相應的處理輸出。
二、當子進程被kill子後會向客戶端發送FIN,客戶端收到以後回覆ACK,關閉已經鏈接的套接字。
三、關閉鏈接以後,當再次向服務端發送字符時,服務端返回reset,因而客戶端進程退出。
四、若是客戶端接收到reset的以後再發送字符,那麼這時候內核就會向該進程發送一個SIGPIPE信號,終止進程。
本程序涉及到前3點,不會出現第4點狀況,由於當讀到的字符爲null時,客戶進程將打印出信息,並推出。注意當沒有輸入任何字符按回車以後,fgets將讀到換行符\n,只有客戶端收到EOF時,表示文件結束符,此時fgets不會讀到任何字符,即爲null。
一、 服務端:查找子進程pid並kill
2、客戶端:終止以後再發送字符,返回服務端提早終止信息
總結:
一、 fgets若從標準輸入stdin讀字符串,當所給的字符串長度小於MAXLINE-1時,會讀入換行符並在最後加上\0,不然,讀入MAXLINE-1個字符加\0;
二、 read,write寫文件以EOF爲結束,寫字符串別忘了寫上\0
三、 strlen不包括\0長度,因此在write時要strlen+1