基于C语言的Linux下的TCP服务器开发小白教程

TCP 服务器 linux 小白教程
donatello
发布时间: 2018-05-22
阅读: 1661

       日常开发中,我们经常会遇到用Linux主机充当TCP服务器的情况,只要是带网口或者带WIFI的Linux系统,都可以充当TCP/UDP的服务器或者客户端。Liunx环境下进行TCP通信要比PC更为简单,因为在Linux的系统API中,就自带了TCP/UDP Socket通信的函数,而如果Windows要进行TCP/UDP通信,还要安装相应的软件库来启动,加载相关的开发环境(如QT、MFC等)。我这篇文章的针对人群是Linux和C语言基础非常浅显的小白,语言风格尽量通俗易懂,力求小白都可以看懂、理解。


    TCP通信的上下级层次结构就不多赘述了,TCP属于OSI参考模型中的传输层,向上一级的应用层协议有HTTP、FTP等,这些协议都必须以以TCP Socket通信为基础的。TCP往下的网络层的协议是IP协议,TCP通信必须建立在IP层,IP层又是建立在数据链路层和物理层这些底层的硬件基础上。


1.JPG
   

  接下来重点讲的是Linux TCP通信的流程。TCP服务器在一开始的时候必须要进行socket()、bind()、listen()、accept()的操作。socket()操作是建立TCP的收发套接字,套接字是通信的数据基础;bind()操作是将socket与TCP服务器绑定,这个操作主要跟TCP服务器开放的端口有关,当端口被占用时则bind()操作失败;listen()顾名思义,就是接听TCP客户端的连接请求;accept()则是接受TCP客户端的请求。accept()是阻塞操作,如果以我们常规的思维来理解,则accept()和listen()应该是同一个东西并且阻塞,因为我们很少会遇到TCP服务器拒绝TCP客户端连接请求的,但是程序是要求严谨的,listen()和accept()操作都必须有并且顺序不能随意改变。


2.JPG


    操作建立之后,就是recv()接收((有些文章会把recv()写成read(),意义完全一样))和send()发送操作了(有些文章会把send()写成write(),意义完全一样),其中recv()是阻塞操作,因此Linux要实现TCP服务器的接收和发送一体,就必须使用多线程应用。在Linux中,多线程是真正的多线程(除了单核心单线程CPU),并不是时间片轮转调度,新开辟出来的一个线程,跟原有的线程是完全独立运行的,线程间通信必须要使用信号量、消息队列或者邮箱,在本次TCP通信中,由于TCP服务器要同时进行收发操作,且收发操作完全独立,最关键是接收操作还是阻塞的,那就必须启用多线程,详情看下面的代码。


    首先是完成TCP服务器的准备工作,第一步是设定TCP服务器的工作模式(默认是AF_INET,无需深究,官方设定)、IP地址和端口:

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

    这里我使用argv输入字符串进行IP地址和端口的设置,这个argv是程序执行时输入的内容,以空格分隔,如Linux下执行main程序:
#./main aaa bbb
argv[0]就是"./main",argv[1]就是"aaa",argv[2]就是"bbb"。
    然后依次执行socket()、bind()、listen()、accept()四个操作:
 
   int ret,fd_socket;
fd_socket=socket(AF_INET,SOCK_STREAM,0);
if(fd_socket==-1)
{
printf("套接字初始化失败!\n");
return -1;
}
ret=bind(fd_socket,(struct sockaddr *)&bindaddr,addrsize);
if(ret==-1)
{
printf("套接字绑定失败!\n");
return -1;
}
ret=listen(fd_socket,5);
if(ret==-1)
{
printf("服务器监听失败!\n");
return -1;
}
newsock=accept(fd_socket,(struct sockaddr *)&boyaddr,&addrsize);

在实际应用中,最有可能导致程序提前退出的只有bind()操作,那就是端口被占用。这里accept()操作是阻塞的,如果一直没有TCP客户机连接,程序就会一直阻塞在这个地方。
    然后是建立多一个线程出来:
pthread_t id1;
pthread_create(&id1,NULL,Scanf_Thread,NULL);


在main()文件的其它地方添加线程内容,这里我的设计是先输入一个整数,表示将要发送数据的长度,然后动态分配一个内存空间给发送缓冲区用,不足补\0,多出则丢弃:
int newsock;
void *Scanf_Thread(void *arg)
{
int len;
char *s;
while(1)
{
bzero(s,0);
scanf("%d",&len);
s=(char *)malloc(len);
scanf("%s",s);
send(newsock,s,len,0);
free(s);
}
}
main函数的while(1)里面添加:
while(1)
{
bzero(recvbuf,100);
recv(newsock,recvbuf,100,0);
printf("客户端发来数据:%s\n",recvbuf);
}

其中recvbuf是char字符串。这样,一个最简单的TCP服务器收发程序就搞定了,先来看看效果,运行程序,并输入IP 169.254.122.7,端口6666:


3.JPG


收发助手设置为TCP客户端模式,输入IP 169.254.122.7,端口6666,点击连接:

4.JPG


客户端向服务器发送多条数据:

5.JPG
服务器向客户端发送多条数据:

6.JPG


原创作品,未经权利人授权禁止转载。详情见转载须知 举报文章

点赞 (1)
donatello
评论(0)

登录后可评论,请 登录注册

相关文章推荐
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回

我要举报该内容理由

×
请输入您举报的理由(50字以内)