TCP_Server

一,socket编程

在TCP/IP协议中,IP地址+TCP/UDP的端口号,唯一地标识了网络通讯中的一个进程,“IP地址+端口号”就称为socket。

二,TCP_Server

2.1 基于多线程

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

static void userHelp(const char* str)
{
 printf("%s [local_ip] [local_port]",str);
}

int startup(const char* _ip,int _port)
{
  //socket打开一个 网络通讯端口,其中AF_INET:表示IPV4,SOCK_STREAM:表示面向流的传输, 
  //protocol参数默认选择为0 
 int sock=socket(AF_INET,SOCK_STREAM,0);
 if(sock==-1)
 {
  perror("socket()");
  exit(-1);
 }
 struct sockaddr_in local;
 local.sin_family=AF_INET;
 local.sin_port=htons(_port);
 local.sin_addr.s_addr=inet_addr(_ip);
//bind的作用是将参数sock与local绑定在一起 
 //使sock这个文件描述符监听local所描述的地址与端口号 
 //成功返回0,失败返回-1 
 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) //success zero is return
   {
    perror("bind()");
    exit(-2);

   }
//listen声明sock处于监听状态,并且最多允许5个客户端处于连接等待状态  
//如果收到更多的请求便忽略 
if(listen(sock,5)==-1)  //add listener
{
 perror("listen()");
 exit(-3);
}
  return sock;
}

void* handlerRequest(void *arg)
{
 int new_fd=(int)arg;
 printf("new_fd=%d\n",new_fd);
 while(1)
 {
  char buffer[1024];
 ssize_t s=read(new_fd,buffer,sizeof(buffer));
  if(s==-1)
     {
      perror("read");

     } 
  if(s>0)
  {
   buffer[s]='\0';
   printf("client:%s\n",buffer);
   write(new_fd,buffer,strlen(buffer));
  }
  else
  {
   printf("read done...break\n");
   break;
  }

 }
}
 int main(int argc,char* argv[])
{
if(argc!=3)
 {
  userHelp(argv[0]);
  return 1;
 }
int listen_sock=startup(argv[1],atoi(argv[2]));
while(1)
{
 struct sockaddr_in client;
 socklen_t len=sizeof(client);
 //accept阻塞式等待,用来接收连接 
 int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);
 if(new_fd<0)
 {
  perror("accept()");
  continue;
 }
 printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
 pthread_t id;
 pthread_create(&id,NULL,handlerRequest,(void*)new_fd);
 pthread_detach(id);
//return 0;
}
return 0;
}

2.2 基于多进程

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

static void userHelp(const char* str)
{
 printf("%s [local_ip] [local_port]",str);
}

int startup(const char* _ip,int _port)
{
 int sock=socket(AF_INET,SOCK_STREAM,0);
 if(sock==-1)
 {
  perror("socket()");
  exit(-1);
 }
 struct sockaddr_in local;
 local.sin_family=AF_INET;
 local.sin_port=htons(_port);
 local.sin_addr.s_addr=inet_addr(_ip);

 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) //success zero is return
   {
    perror("bind()");
    exit(-2);

   }
if(listen(sock,5)==-1)  //add listener
{
 perror("listen()");
 exit(-3);
}
  return sock;
}

 int main(int argc,char* argv[])
{
if(argc!=3)
 {
  userHelp(argv[0]);
  return 1;
 }
int listen_sock=startup(argv[1],atoi(argv[2]));
while(1)
{
 struct sockaddr_in client;
 socklen_t len=sizeof(client);
 int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);
 if(new_fd<0)
 {
  perror("accept()");
  continue;
 }

//建立进程 
pid_t id=fork();
if(id<0)
{
 perror("fork");
 close(new_fd);
}
else if(id==0) 
{       
  close(listen_sock);
  if(fork()>0)
  {
   close(new_fd);         
   exit(0);
  }
while(1)
{
 char buf[1024];
 ssize_t s=read(new_fd,buf,sizeof(buf)-1);
 if(s>0)
 {
 printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));      
  buf[s]=0;
  printf("client:%s\n",buf);
  write(new_fd,buf,strlen(buf));
 }
 else
 {
 printf("read done...,break\n");
 break;
 }
}
close(new_fd);

}
}
return 0;
}

三,client

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
static void userHelp(char* str)
{
 printf("%s [server_ip] [server_port]",str);
}
int main(int argc,char * argv[])
{
 if(argc!=3)
 {
  userHelp(argv[0]);
  return 1;
 }

 int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
 perror("sock()");
 exit(-1);

}
struct sockaddr_in client;
client.sin_family=AF_INET;
client.sin_port=htons(atoi(argv[2]));
client.sin_addr.s_addr=inet_addr(argv[1]);

if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
{
 perror("connect()");
 exit(-2);

}
while(1)
{
char buffer[1024];
printf("please input:");
fflush(stdout);
ssize_t s=read(0,buffer,sizeof(buffer)-1);
if(s>0)
{
 buffer[s-1]=0;
 write(sock,buffer,strlen(buffer));
 ssize_t _s=read(sock,buffer,sizeof(buffer)-1);
 if(_s>0)
 {
 buffer[_s]=0;
 printf("server echo#%s\n",buffer);
 }

}
}
return 0;
}

四,测试结果(基于多进程版本)

clent:

这里写图片描述

server:

这里写图片描述

五,server,bind()失败原因调研

在建立连接后,我们发现如果单方面的结束掉server端,再在相同的端口运行server端程序将会出现以下问题:

这里写图片描述
这是因为,虽然server的应用程序终止了,但TCP的连接并没有完全断开,因此不能再次监听同样的server端口;我们用“netstat”命令查看一下:
这里写图片描述

我们发现8080端口还在被占用,这是因为TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL的时间后才能回到closed状态,所以当我们在连接中用“ctrl+c”结束掉server后,还需要进行等待,所以端口还是会处于监听状态。

<think>好的,我现在要解释用户提供的Makefile代码。首先,我需要回忆Makefile的基本结构和语法,确保自己理解正确。用户给出的代码看起来是一个用于编译TCP客户端和服务器的Makefile。接下来,我需要逐行分析每个部分的作用。 首先看第一行:CC=gcc。这应该是设置编译器变量,指定使用gcc作为C编译器。然后是CFLAGS=-Wall,这里设置了编译选项,启用所有警告。不过注意到在后面的规则中,直接用了-Wall而不是引用CFLAGS变量,这可能是个问题或者用户有意为之? 接下来是all目标,依赖tcp_client和tcp_server。这说明默认情况下,执行make命令会构建这两个可执行文件。不过当前的Makefile中只有tcp_server的规则,没有tcp_client的规则,这可能是个遗漏或者用户没给出全部代码?需要指出这一点。 然后看tcp_server目标,它依赖于tcp_server.c,编译命令使用gcc并链接pthread库。这里需要注意编译命令是否使用了CFLAGS变量,但实际用的是-Wall,可能用户应该用$(CFLAGS)来保持一致性。另外,-lpthread的位置是否正确?通常链接库应该放在命令末尾,这里没问题。 clean目标用于删除生成的可执行文件,但同样缺少tcp_client的清理,这可能是因为tcp_client的规则不存在,所以clean里可能也没必要,或者用户需要补充。 总结问题点:缺少tcp_client的构建规则,CFLAGS变量未被使用,可能导致后续维护不便。需要向用户指出这些潜在问题,同时解释现有的结构。同时,确认Makefile中的缩进是否是制表符,因为这是Makefile的语法要求,用户提供的代码中缩进正确。 最后,用中文清晰分点解释各部分功能,并指出可能的改进建议,确保用户理解整个Makefile的结构和潜在问题。</think>这是一个用于编译TCP客户端/服务器程序的Makefile,我为您逐步分解其结构和功能: 1. **变量定义** - `CC = gcc`:定义编译器变量,指定使用GCC编译C程序 - `CFLAGS = -Wall`:设置编译选项,`-Wall`表示启用所有警告信息 2. **默认目标** - `all: tcp_client tcp_server`:定义默认构建目标,当直接运行`make`时会编译这两个可执行文件 3. **服务器编译规则** ```makefile tcp_server: tcp_server.c $(CC) tcp_server.c -Wall -o tcp_server -lpthread ``` - 目标文件`tcp_server`依赖`tcp_server.c`源文件 - 编译命令使用gcc编译器,`-lpthread`表示链接POSIX线程库 - 注意这里直接使用`-Wall`而未使用`$(CFLAGS)`变量 4. **清理规则** - `clean: rm tcp_server tcp_client`:删除生成的可执行文件 ⚠️ **需要注意的问题**: 1. 缺失`tcp_client`的构建规则:当前文件只定义了server的编译规则,client部分需要补充 2. 推荐使用变量:应将`-Wall`替换为`$(CFLAGS)`以保持统一 3. 可添加`.PHONY`声明:建议添加`.PHONY: all clean`避免与同名文件冲突 **建议的完整Makefile**: ```makefile CC = gcc CFLAGS = -Wall -Wextra .PHONY: all clean all: tcp_client tcp_server tcp_server: tcp_server.c $(CC) $(CFLAGS) $< -o $@ -lpthread tcp_client: tcp_client.c $(CC) $(CFLAGS) $< -o $@ clean: rm -f tcp_server tcp_client ``` **使用说明**: - 编译:`make` 或 `make all` - 清理:`make clean` - 单独编译server:`make tcp_server`
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值