一、TCP 服务器的创建
在 Linux 上创建一个简单的 tcp 服务器步骤如下:
①创建套接字
②将套接字绑定到 IP 地址和端口号
③监听来自客户端的连接
④接受连接并创建新的套接字用于与客户端通信
⑤通过新建的套接字发送和接收数据
⑥关闭套接字
流程框图如下:
根据以上介绍可以创建tcp server的示例,分为服务器-单客户端和服务器-多客户端。
二、服务器-单客户端示例
tcp server示例代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h> //inet_addr()
#include <unistd.h>//close()
#define MY_PRINTF(argv) do{\
printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
printf("%s\r\n",argv);\
}while(0);
#define MYPORT 5000
//tcp server demo
int main(int argc,char *argv[])
{
int sockefd;
int sockenewfd;
int ret;
int enable=1;
struct sockaddr_in my_addr;//本地地址-服务器
struct sockaddr_in remote_addr;//远端地址-客户端
int remote_addr_len;
char buf[1024];
sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
if(sockefd<0)
{
MY_PRINTF("socket err !! ");
return -1;
}
if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1)
{
close(sockefd);
MY_PRINTF(" setsockopt err !! ");
return -1;
}
MY_PRINTF("socket ok !! ");
//本地地址
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(MYPORT);//0:随机端口
my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip // inet_addr():IP 地址的字符串转换成一个无符号长整型
bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));
ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定 //转换数据类型
if(ret<0)
{
close(sockefd);
MY_PRINTF("bind err !! ");
return -1;
}
MY_PRINTF("bind ok !! ");
ret=listen(sockefd,5);//监听
if(ret<0)
{
close(sockefd);
MY_PRINTF("listen err !! ");
return -1;
}
MY_PRINTF("listen ok !! ");
sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
if(sockenewfd<0)
{
close(sockefd);
MY_PRINTF("accept err !! ");
return -1;
}
MY_PRINTF("accept ok !! ");
printf("%d\r\n",ntohs(remote_addr.sin_port)); //端口号
printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址 //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来
for(;;)
{
ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
if(ret<0)
{
close(sockenewfd);
MY_PRINTF("recv err !! ");
break;
}
else if(ret==0)
{
close(sockenewfd);
MY_PRINTF("close !! ");
perror("close sockenewfd");
break;
}
MY_PRINTF("recv ok !! ");
MY_PRINTF(buf);
ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
if(ret<0)
{
MY_PRINTF("send err !! ");
}
MY_PRINTF("send ok !! ");
MY_PRINTF(buf);
}
close(sockefd);//关闭
perror("close sockefd");
return0;
}
程序运行只允许单个客户端连接通讯。测试如下:
服务端启动,等待连接,如下:
客户端连接,如下:
服务端获取到客户端连接,如下:
客户端发送数据,并接收到服务端返回,如下:
服务端显示:
三、服务器-多客户端
这里介绍两种方法,方法一:使用多线程;方法二:使用select方法。
①方法一多线程,测试代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h> //inet_addr()
#include <unistd.h>//close()
#define MY_PRINTF(argv) do{\
printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
printf("%s\r\n",argv);\
}while(0);
#define MYPORT 5000
//tcp server demo
void *myfun_thread(void *socketInfo)
{
char buf[1024];
int ret;
int sockenewfd=(int)socketInfo;
for(;;)
{
ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
if(ret<0)
{
close(sockenewfd);
MY_PRINTF("recv err !! ");
perror("close sockenewfd");
break;
}
else if(ret==0)
{
close(sockenewfd);
MY_PRINTF("close !! ");
perror("close sockenewfd");
break;
}
MY_PRINTF("recv ok !! ");
MY_PRINTF(buf);
ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
if(ret<0)
{
MY_PRINTF("send err !! ");
}
MY_PRINTF("send ok !! ");
MY_PRINTF(buf);
}
pthread_exit(NULL);
}
int main(int argc,char *argv[])
{
int sockefd;
int sockenewfd;
int ret;
int enable=1;
pthread_t threadRx;
struct sockaddr_in my_addr;//本地地址-服务器
struct sockaddr_in remote_addr;//远端地址-客户端
int remote_addr_len;
sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
if(sockefd<0)
{
MY_PRINTF("socket err !! ");
return -1;
}
if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1)
{
close(sockefd);
MY_PRINTF(" setsockopt err !! ");
return -1;
}
MY_PRINTF("socket ok !! ");
//本地地址
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(MYPORT);//0:随机端口
my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip // inet_addr():IP 地址的字符串转换成一个无符号长整型
bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));
ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定 //转换数据类型
if(ret<0)
{
close(sockefd);
MY_PRINTF("bind err !! ");
return -1;
}
MY_PRINTF("bind ok !! ");
ret=listen(sockefd,5);//监听
if(ret<0)
{
close(sockefd);
MY_PRINTF("listen err !! ");
return -1;
}
MY_PRINTF("listen ok !! ");
while(1)
{
sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
if(sockenewfd<0)
{
MY_PRINTF("accept err !! ");
break;
}
MY_PRINTF("accept ok !! ");
printf("%d\r\n",ntohs(remote_addr.sin_port)); //端口号
printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址 //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来
pthread_create(&threadRx, NULL, myfun_thread, (void *)sockenewfd);
sleep(1);
}
close(sockefd);//关闭
perror("close sockefd");
return 0;
}
测试结果:
服务端:
客户端: