资深程序员大牛带你用C语言实现网络通信 流程实例

C语言永远不会过时

其实学编程关键是学习其思想,如果你精通了一门,再去学其他的时候也很容易上手。C不会过时的,尤其是在unix、linux操作平台上,学好C是必须的。

C跟C++在很多方面也是兼容的,c是c++的基础。

再者c能从很大的程度上帮你了解计算机的发展史,数据结构等方面的知识,很多软件、甚至操作系统中的很大部分是用c来实现的。

还有一些电器芯片的程序,比如电冰箱内制冷系统……可以说用c可以解决一切可能遇到的问题,关键是你要能精通它。

所以放开手脚去大胆的学吧,c永远不会过时

小编给大家推荐一个学习氛围超好的地方,C/C++交流企鹅裙:三四一六三六七二七!适合在校大学生,小白,想转行,想通过这个找工作的加入。裙里有大量学习资料,有大神解答交流问题,每晚都有免费的直播课程

主要函数:

------------------------------------------

TCP实现服务器与客户端的通信流程

//服务器端---服务器是一个被动的角色

1.socket //买一个手机

2.bind //SIM卡 绑定一个手机号(ip+port)

3.listen //待机(等待电话打入)

4.accept //接听电话

5.read/write //通话

6.close //挂机

//客户端---客户端是一个主动发起请求的一端

1.socket //买一个手机

2.bind(可选的) //SIM卡(绑定号码)

3.connect //拨打电话

4.read/write //通话

5.close //挂机

//1.socket ---- 插口

int socket(int domain, int type, int protocol);

功能: 创建通信的一端 (socket)

参数:

@domain //"域" --范围

AF_INET //IPV4 协议的通信

@type SOCK_STREAM //TCP (流式套接字)

@protocol 0 //LINUX下 流式套接字 ==>TCP

//协议

返回值:

成功 对应的socket文件描述符

失败 返回-1

注意:

文件描述符:

实际上就是 创建好的 socket的一个标识符

//2.bind

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:

给指定的 socket 绑定地址信息

参数:

@sockfd //表示操作的socket

@addr //填充的地址信息(ip + port)

@addrlen //地址信息结构体的大小

返回值:

成功 0

失败 -1

//通用的地址结构

struct sockaddr {

sa_family_t sa_family; //AF_INET //IPV4的协议

char sa_data[14];//(ip+port)

}

//网络通信的地址结构(internet)

struct sockaddr_in {

sa_family_t sin_family; /* address family: AF_INET */

in_port_t sin_port; /* port in network byte order */

struct in_addr sin_addr; /* internet address */

};

/* Internet address. */

struct in_addr {

uint32_t s_addr; /* address in network byte order */

};

//1.定义一个 地址结构体变量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函数

//2.之后进行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(8888);

addr.sin_addr.s_addr = inet_addr("127.0.0.1");

//127.0.0.1 是回环测试的地址

//3.进行绑定

if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr))

{

perror("bind fail");

return 0;

}

//3.listen --- 设置监听 ---作用:让操作系统监控是否有客户端发起连接

int listen(int sockfd, int backlog);

功能:

设置监听

参数:

@sockfd //监听套接字

@backlog //监听队列的大小

返回值

成功 0

失败 -1

listenfd

--------------监听队列------------------

fd1 fd2 fd3 fd4

|

-|--------------------------------------

|

\---->建立好连接的套接字

accept函数获取已连接的套接字 返回对应

的标识符

|--->后面的读写操作 都是通过这个标识符

进行的

-----------------------------------------------

accept(); //accept 从监听队列中获得已连接的的socket,返回一个标示符来表示已连接的socket

//后续通过已连接的socket进行通信

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能: 获取连接

参数:

@sockfd //监听套接字的fd(标识符)

@addr //来电显示(保存对端的地址信息)(ip+port)

@addrlen //表示 addr 参数对应类型的大小,值结果参数 --- 就是在用的时候,必须先赋一个初值,最后函数调用完成

//通过该参数,返回一个结果值

返回值:

成功 已连接的socket的标识符

失败 -1

//connect ---发起连接

int connect(

int sockfd, //表示 进行通信的 socket的标识符

const struct sockaddr *addr, //对端的地址信息(ip+port)

socklen_t addrlen); //表示的是 addr 参数类型的长度

参数:

@sockfd //通过socket函数获得的fd

@addr //服务器端的地址

@addrlen //参数addr类型的大小

//数据流向 fd --> buf (count 表示一次读取多少个字节)

ssize_t read(int fd, void *buf, size_t count);

//数据流向 buf--> fd (count 表示一次写多少个字节)

ssize_t write(int fd, const void *buf, size_t count);

参数:

@fd 就是要操作的 socket对应的 标示符

@buf 保存数据的一块内存首地址

@count 一次操作的字节数

confd

char buf[] = "hello QCXY\n";

write(confd,buf,strlen(buf)); //写 数据到socket中

//读数据出来

char rbuf[1024] = ; //表示申请了一块1024个字节大小

//的内存空间

read(confd,rbuf,sizeof(rbuf)); //读取数据

//练习:

实现 客户端 向服务器发送数据

服务器回发数据的功能

client ----- server

scanf(); ---(1)----> read 之后printf

read

printf

循环做,结束条件

当客户端输入 "quit"字符串时 客户端结束

怎么判断客户端读到的是"quit"

c语言中

"quit" == buf; (X) //不能这么写

//字符串的比较函数

strcmp("quit",buf);

strncmp("quit",buf,4);

客户端的程序:

#include

#include /* See NOTES */

#include

#include

#include

#include

#include

//./client 127.0.0.1 8888

int main(int argc, const char *argv[])

{

int fd;

int ret = 0;

char buf[1024] = ;

char rbuf[1024] = ;

//处理命令行参数

//1.socket(手机)

//2.bind(电话卡)

//3.connect (拨打电话)

//处理命令行参数

if(argc != 3)

{

printf("Usage: %s

\n",argv[0]);

return -1;

}

//1.socket(手机)

fd = socket(AF_INET,SOCK_STREAM,0);

if(fd

{

perror("socket fail");

return -1;

}

printf("fd = %d\n",fd);

//2.bind(电话卡)---绑定的是客户端自己的地址信息

//客户端地址信息

//1.定义一个 地址结构体变量

struct sockaddr_in cli_addr;

bzero(&cli_addr,sizeof(cli_addr)); //清0的函数

//2.之后进行信息填充

cli_addr.sin_family = AF_INET;

cli_addr.sin_port = htons(7777);

cli_addr.sin_addr.s_addr = inet_addr(argv[1]);

if(bind(fd,(struct sockaddr*)&cli_addr,sizeof(cli_addr))

{

perror("bind fail");

return -1;

}

//服务器端的地址信息

//1.定义一个 地址结构体变量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函数

//2.之后进行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(atoi(argv[2]));

addr.sin_addr.s_addr = inet_addr(argv[1]);

//3.connect (拨打电话)

if(connect(fd,(struct sockaddr*)&addr,sizeof(addr))

{

perror("connect fail");

return -1;

}

printf("connect success\n");

//通信过程

while(1)

{

//客户端从键盘获得数据

//数据流向stdin --> buf

fgets(buf,sizeof(buf),stdin); //stdin表示是从键盘获得数据

//发送给服务器

write(fd,buf,strlen(buf));

//接受服务器回发的消息

ret = read(fd,rbuf,sizeof(rbuf));

//如果回发的消息是

//quit

//则结束

rbuf[ret] = '\0';

printf("rbuf = %s\n",rbuf);

if(strncmp("quit",buf,4) == 0)

{

close(fd);

break;

}

}

return 0;

}

服务端

#include

#include /* See NOTES */

#include

#include

#include

#include

//./server 127.0.0.1 8888

int main(int argc, const char *argv[])

{

int fd = 0;

int connfd = 0;

int ret = 0;

char buf[1024] = ;

//处理命令行参数

if(argc != 3)

{

printf("Usage: %s

\n",argv[0]);

return -1;

}

//1.socket 创建套接字

fd = socket(AF_INET,SOCK_STREAM,0);

if(fd

{

perror("socket fail");

return -1;

}

printf("fd = %d\n",fd);

//2.绑定

//1.准备地址信息

//2.绑定

//

//1.定义一个 地址结构体变量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函数

//2.之后进行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(atoi(argv[2]));

addr.sin_addr.s_addr = inet_addr(argv[1]);

//127.0.0.1 是回环测试的地址

//3.进行绑定

if(bind(fd,(struct sockaddr*)&addr,sizeof(addr))

{

perror("bind fail");

return 0;

}

printf("bind success\n");

//4.设置监听

if(listen(fd,5)

{

perror("listen fail");

return -1;

}

struct sockaddr_in peer_addr;

socklen_t addrlen = sizeof(peer_addr);

//5.获取连接 -- 接听电话

while(1) //可以不断接受客户端的请求

{

//connfd = accept(fd,NULL,NULL);

connfd = accept(fd,(struct sockaddr*)&peer_addr,&addrlen);

if(connfd

{

perror("accept fail");

return -1;

}

printf("connfd = %d\n",connfd);

printf("-----------------------\n");

printf("ip = %s\n",inet_ntoa(peer_addr.sin_addr));

printf("port = %d\n",ntohs(peer_addr.sin_port));

printf("-----------------------\n");

//通信过程

//效果实现数据回发

while(1)

{

//read 与 write 的返回值 大于0 时表示的是

//一次成功操作到的字节数

ret = read(connfd,buf,sizeof(buf));

//hello

buf[ret] = '\0'; //添加'\0'--转换成字符串

printf("buf = %s\n",buf);//字符串打印 需要

//结束标志 '\0'

if(ret == 0 || strncmp(buf,"quit",4) == 0)

{

close(connfd);

break;

}

write(connfd,buf,ret);

}

} //telnet

return 0;

}

补充:

可以用如下函数替代read,write

ssize_t recv(int sockfd, void* buf,size_t len,int flags);

ssize_t send(int sockfd,const void *buf,size_t len,int flags);

@sockfd //进行操作的socket的文件描述符

@buf //保存数据的首地址

@len //一次操作的字节数

@flags //标志0--默认的操作方式(阻塞)

返回值:

成功 成功操作的字节数

失败 -1&errno

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180405A06HT300?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券