一、正常启动
1.启动服务器
./tcpserver
gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:10000 0.0.0.0:* LISTEN
服务器启动后,经过socket,bind,listen,阻塞于accept,等待客户端的连接。
gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:10000 0.0.0.0:* LISTEN
2.启动客户端
./tcpclient
客户端调用socket,connect,connect引发三次握手。当三次握手结束,客户端的connect和服务器的accept均返回,连接建立。
(1)客户端调用str_cli阻塞于fgets
(2)服务器父进程阻塞于accept,等待新的客户来连接
(3)
服务器子进程调用str_echo阻塞于read。
gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:10000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:10000 127.0.0.1:49277 ESTABLISHED
tcp 0 0 127.0.0.1:49277 127.0.0.1:10000 ESTABLISHED
使用ps命令来查看进程的状态和关系
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/12 -o pid,ppid,tty,stat,args,wchan
PID PPID TT STAT COMMAND WCHAN
17561 23965 pts/12 S+ ./tcpclient n_tty_read
23965 23957 pts/12 Ss -bash wait
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/9 -o pid,ppid,tty,stat,args,wchan
PID PPID TT STAT COMMAND WCHAN
15339 15338 pts/9 Ss -bash wait
17546 15339 pts/9 S+ ./tcpserver inet_csk_accept-----------服务器父进程
17562 17546 pts/9 S+ ./tcpserver sk_wait_data
-----------服务器子进程
二、正常终止
连接建立以后,客户端输入啥,立即回射该内容
当在客户端输入CTRL+D(终端的EOF字符)时:
(1)客户端str_cli函数中的fgets返回NULL,str_cli函数退出,最终客户端退出,客户端进程退出,会close打开的socket描述符,这会导致客户端发送FIN报文给服务器。
(2)服务器收到客户端的FIN报文,read函数返回0,导致str_echo函数退出,最终服务端的子进程退出,关闭对应的socket套接字,发送FIN报文给客户端,并发送SIGCHLD信号给父进程,如果父进程没有响应该信号,则该子进程编程僵死进程。
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/9 -o pid,ppid,tty,stat,args,wchan
PID PPID TT STAT COMMAND WCHAN
15339 15338 pts/9 Ss -bash wait
17546 15339 pts/9 S+ ./tcpserver inet_csk_accept
17562 17546 pts/9 Z+ [tcpserver] exit --------------------------------此为僵死进程
(3)客户端响应FIN报文,发送ACK,进入TIME_WAIT状态
tcpserver.c
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <signal.h>
-
#include <sys/socket.h>
-
#include <sys/types.h>
-
#include <sys/wait.h>
-
#include <errno.h>
-
#include <netinet/in.h>
-
#include <arpa/inet.h>
-
#include <sys/wait.h>
-
#include <strings.h>
-
#include "readn.h"
-
-
/*read函数从套接字读入数据,writen函数把其中的内容回射给客户。如果客户关闭连接,那么接收到客户的FIN将导致服务器子进程的read函数返回0,这又导致str_echo函数的返回,从而终止了子进程*/
-
void str_echo(int fd)
-
{
-
int n;
-
char recvline[MAXLINE];
-
again:
-
while((n = read(fd,recvline,MAXLINE))>0) {
-
writen(fd,recvline,n);
-
}
-
if(n < 0 && errno == EINTR) {
-
goto again;
-
}else if (n < 0) {
-
printf("read error\n");
-
}
-
}
-
-
int main(int argc,char *argv[])
-
{
-
int ret = 0;
-
int listen_fd;
-
int conn_fd;
-
struct sockaddr_in server_addr;
-
struct sockaddr_in client_addr;
-
pid_t pid;
-
pid_t child_pid;
-
int status;
-
socklen_t len;
-
-
listen_fd = socket(AF_INET,SOCK_STREAM,0);
-
-
if(listen_fd < 0) {
-
printf("socket error\n");
-
return -1;
-
}
-
-
bzero(&server_addr,sizeof(struct sockaddr_in));
-
-
server_addr.sin_family = AF_INET;
-
server_addr.sin_port = htons(10000);
-
//server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
ret = inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr.s_addr);
-
if(ret < 0) {
-
printf("inet_pton error\n");
-
close(listen_fd);
-
return ret;
-
}else if(ret == 0) {
-
printf("the second parameter of inet_addr is null\n");
-
}
-
-
if(bind(listen_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1) {
-
printf("bind error\n");
-
close(listen_fd);
-
return -1;
-
}
-
-
-
if(listen(listen_fd,1024) == -1) {
-
printf("listen error\n");
-
close(listen_fd);
-
return -1;
-
}
-
-
for(;;) {
-
len = sizeof(client_addr);
-
conn_fd = accept(listen_fd,(struct sockaddr*)&client_addr,&len);
-
if(conn_fd < 0) {
-
printf("accept error,errno=%d\n",errno);
-
if(errno == EINTR)
-
continue;
-
return -1;
-
}
-
-
pid = fork();
-
-
if(pid < 0) {
-
printf("fork error\n");
-
return -1;
-
}else if(pid == 0) {
-
close(listen_fd);
-
/*TODO::handle on conn_fd*/
-
str_echo(conn_fd);
-
exit(0);
-
}
-
close(conn_fd);
-
child_pid = waitpid(pid,&status,WNOHANG);
-
if(child_pid < 0)
-
printf("wait error\n");
-
}
-
-
return 0;
-
}
tcpclient.c
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <sys/socket.h>
-
#include <string.h>
-
#include <sys/types.h>
-
#include <arpa/inet.h>
-
#include "readn.h"
-
-
void str_cli(FILE *fp,int fd)
-
{
-
char sendline[1024], recvline[1024];
-
-
while(fgets(sendline,1024,fp)) {
-
writen(fd,sendline,strlen(sendline));
-
memset(recvline,0,sizeof(recvline));
-
readline(fd,recvline,sizeof(recvline));
-
fputs(recvline,stdout);
-
}
-
}
-
-
int main(int argc,char* argv[])
-
{
-
int fd;
-
struct sockaddr_in server_addr;
-
int ret = 0;
-
-
-
fd = socket(AF_INET,SOCK_STREAM,0);
-
-
if(fd < 0) {
-
printf("socket error\n");
-
return -1;
-
}
-
-
bzero(&server_addr,sizeof(struct sockaddr_in));
-
-
server_addr.sin_family = AF_INET;
-
ret = inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
-
-
if(ret < 0) {
-
printf("inet_pton error\n");
-
return -1;
-
}else if(ret == 0) {
-
printf("the second parameter of inet_pton is NULL\n");
-
}
-
server_addr.sin_port = htons(10000);
-
-
ret = connect(fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
-
if(ret < 0) {
-
printf("connect error\n");
-
return -1;
-
}
-
-
str_cli(stdin,fd);
-
-
return 0;
-
}
readn.h
-
#ifndef __READN_H__
-
#define __READN_H__
-
#define MAXLINE 1024
-
int readn(int fd, void* buffer, size_t n);
-
int writen(int fd, void* buffer, size_t n);
-
size_t readline(int fd, void * buffer, size_t maxlen);
-
#endif
readn.c
-
#include <unistd.h>
-
#include <signal.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <errno.h>
-
#include "readn.h"
-
-
int readn(int fd, void* buffer, size_t n)
-
{
-
int nread;
-
int nleft;
-
char *ptr;
-
-
ptr = buffer;
-
nleft = n;
-
while(nleft >0) {
-
if((nread=read(fd,ptr,nleft)) < 0){
-
if(errno == EINTR) {
-
nread = 0;
-
continue;
-
}else {
-
return -1;
-
}
-
}else if (nread==0) {
-
break;
-
}
-
ptr += nread;
-
nleft -= nread;
-
}
-
return (n - nleft);
-
}
-
-
int writen(int fd, void* buffer, size_t n)
-
{
-
int nwrite;
-
int nleft;
-
char* ptr;
-
-
ptr = buffer;
-
nleft = n;
-
while(nleft > 0) {
-
if((nwrite = write(fd,ptr,nleft)) < 0) {
-
if(errno == EINTR) {
-
nwrite = 0;
-
continue;
-
}else {
-
return -1;
-
}
-
}else if(nwrite == 0){
-
break;
-
}
-
ptr += nwrite;
-
nleft -= nwrite;
-
}
-
return (n-nleft);
-
}
-
static char *ptr;
-
static int len = 0;
-
char read_buffer[MAXLINE];
-
ssize_t my_read(int fd, char* buffer)
-
{
-
int nread;
-
-
if(len <= 0) {
-
again:
-
if((nread = read(fd,read_buffer,MAXLINE)) < 0) {
-
if(errno == EINTR)
-
goto again;
-
return -1;
-
}else if(nread == 0) {
-
return 0;
-
}
-
ptr = read_buffer;
-
len = nread;
-
}
-
*buffer = *ptr++;
-
len--;
-
return 1;
-
}
-
-
size_t readline(int fd, void * buffer, size_t maxlen)
-
{
-
int i;
-
int nread;
-
char c;
-
char *ptr;
-
-
ptr=buffer;
-
for(i = 0;i < maxlen;i++) {
-
again:
-
if((nread = my_read(fd,&c)) == 1){
-
*ptr++ = c;
-
if(c == '\n')
-
break;
-
}else if(nread == 0) {
-
*ptr = 0;
-
return i-1;
-
}else {
-
if(errno == EINTR) {
-
goto again;
-
}else {
-
return -1;
-
}
-
}
-
}
-
*ptr = 0;
-
return i;
-
}
编译运行:
gcc -g readn.c tcpclient.c -o tcpclient -Wall
gcc -g readn.c tcpserver.c -o tcpserver -Wall
SERVER端:
./tcpserver
client端:
./tcpclient
abc
abc
阅读(952) | 评论(0) | 转发(0) |