导:网络套接字编程-TCP/UDP开发实例
百科资料:http://www.baike.com/wiki/%E5%A5%97%E6%8E%A5%E5%AD%97
1.TCP通讯实例=====================================================================
TCP 是一种面向连接的、可靠的、基于 IP 的传输层协议。通过 TCP 可以保证我们传送的数据的正确性。
(1)通讯流程梳理
Linux 下网络通信程序基本上都是采用 socket 的方式。socket 起源于 Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开 open->读写 read/write->关闭 close”模式来操作。Socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些socket 函数就是对其进行的操作(开/关/读/写/建)。说白了 socket 是应用程序与 TCP/IP协议族通信的中间软件抽象层,它是一组接口。
(2)代码分析
服务器端:SBLARWRC
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
int sfp, nfp, num = 0;
struct sockaddr_in s_add,c_add;
int sin_size;
unsigned short portnum=0x8888;//端口1对1
char buffer[100] = {0};
printf("Hello,welcome to my server !\r\n");
sfp = socket(AF_INET, SOCK_STREAM, 0); //1.s-调用 socket 函数创建一个套接字
if(-1 == sfp)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;使用 IPv4 协议
s_add.sin_addr.s_addr=htonl(INADDR_ANY);允许任何地址
s_add.sin_port=htons(portnum);设置端口号
//2.b-用bind 绑定函数,使用的是 IPv4 协议族
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("bind fail !\r\n");
return -1;
}
printf("bind ok !\r\n");
if(-1 == listen(sfp,5)) //3.l-调用 listen 监听函数,监听用户的连接请求
{
printf("listen fail !\r\n");
return -1;
}
printf("listen ok\r\n");
sin_size = sizeof(struct sockaddr_in);
//4.a-在监听到用户的请求后调用 accept 函数接受请求(R)
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
printf("accept fail !\r\n");
return -1;
}
printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",
ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
while(1) //567.while(1)中执行 W-R操作
{
memset(buffer, 0, 100);
sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);
send(nfp, buffer, strlen(buffer), 0);//发生函数
usleep(500000);
}
close(nfp);
close(sfp);
return 0;
}
//1.s-调用 socket 函数创建一个套接字
if(-1 == sfp)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;使用 IPv4 协议
s_add.sin_addr.s_addr=htonl(INADDR_ANY);允许任何地址
s_add.sin_port=htons(portnum);设置端口号
//2.b-用bind 绑定函数,使用的是 IPv4 协议族
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("bind fail !\r\n");
return -1;
}
printf("bind ok !\r\n");
if(-1 == listen(sfp,5)) //3.l-调用 listen 监听函数,监听用户的连接请求
{
printf("listen fail !\r\n");
return -1;
}
printf("listen ok\r\n");
sin_size = sizeof(struct sockaddr_in);
//4.a-在监听到用户的请求后调用 accept 函数接受请求(R)
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
printf("accept fail !\r\n");
return -1;
}
printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",
ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
while(1) //567.while(1)中执行 W-R操作
{
memset(buffer, 0, 100);
sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);
send(nfp, buffer, strlen(buffer), 0);//发生函数
usleep(500000);
}
close(nfp);
close(sfp);
return 0;
}
客户端:S C W RC
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char **argv)
{
int cfd;
int recbyte;
int sin_size;
char buffer[1024] = {0};
struct sockaddr_in s_add, c_add;
unsigned short portnum = 0x8888;
printf("Hello,welcome to client!\r\n");
if(argc != 2)
{
printf("usage: echo ip\n");
return -1;
}
cfd = socket(AF_INET, SOCK_STREAM, 0); //1.s-调用 socket 函数创建一个套接字
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET; //ipv4
s_add.sin_addr.s_addr= inet_addr(argv[1]); //地址输入参数-要链接的IP地址
s_add.sin_port=htons(portnum); //端口号,1对1
printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
//2.C-客户端直接链接地址(W)
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
while(1)
{ //3.R-读出来自服务器的数据
if(-1 == (recbyte = read(cfd, buffer, 1024)))
{
printf("read data fail !\r\n");
return -1;
}
printf("read ok\r\nREC:\r\n");
buffer[recbyte]='\0';
printf("%s\r\n",buffer);
}
close(cfd);
return 0;
}
//1.s-调用 socket 函数创建一个套接字
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET; //ipv4
s_add.sin_addr.s_addr= inet_addr(argv[1]); //地址输入参数-要链接的IP地址
s_add.sin_port=htons(portnum); //端口号,1对1
printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
//2.C-客户端直接链接地址(W)
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
while(1)
{ //3.R-读出来自服务器的数据
if(-1 == (recbyte = read(cfd, buffer, 1024)))
{
printf("read data fail !\r\n");
return -1;
}
printf("read ok\r\nREC:\r\n");
buffer[recbyte]='\0';
printf("%s\r\n",buffer);
}
close(cfd);
return 0;
}
(3)概念.什么是 TCP? 什么是 IP?
TCP/IP 是一类协议系统,它是用于网络通信的一套协议集合.TCP/IP 中文名为传输控制协议/因特网互联协议,又名网络通讯协议,是 Internet 最基本的协议、Internet 国际互联网络的基础,由网络层的 IP 协议和传输层的 TCP 协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准;
TCP/IP 协议不是 TCP 和 IP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。从协议分层模型方面来讲,TCP/IP 由四个层次组成:网络接口层、网络层、传输层、应用层。OSI 是传统的开放式系统互连参考模型,该模型将 TCP/IP 分为七层: 物理层、数据链路层(网络接口层)、网络层(网络层)、传输层(传输层)、会话层、表示层和应用层(应用层);
1) 网络接口层:
主要是指物理层次的一些接口,比如电缆等.
2) 网络层:
提供独立于硬件的逻辑寻址,实现物理地址与逻辑地址的转换.
在 TCP / IP 协议族中,网络层协议包括 IP 协议(网际协议),ICMP 协议( Internet 互联网控制报文协议),以及 IGMP 协议( Internet 组管理协议).
3) 传输层:
为网络提供了流量控制,错误控制和确认服务.
在TCP / IP 协议族中有两个互不相同的传输协议: TCP(传输控制协议)和 UDP(用户数据报协议).
4) 应用层:
为网络排错,文件传输,远程控制和 Internet 操作提供具体的应用程序
(4)数据包==>套字节的形象定义
在 TCP / IP 协议中数据先由上往下将数据装包,然后由下往上拆包
在装包的时候,每一层都会增加一些信息用于传输,这部分信息就叫报头,当上层的数据到达本层的时候,会将数据加上本层的报头打包在一起,继续往下传递
在拆包的时候,每一层将本层需要的报头读取后,就将剩下的数据往上传.
这个过程有点像俄罗斯套娃,所以有时候人们也会用俄罗斯套娃来形容这个过程.
2.upd-网络通讯实例===============================================
基于UDP的socket编程:
UDP 是用户数据报协议,它是一种无连接的传输层协议,提供面向事物的简单不可靠信息传送服务,所以在一些网络质量不满意的环境下,UDP 协议数据包丢失会比较严重,会造成数据的丢失。
UDP 的特点是他不属于连接型协议,所以资源消耗小。处理速度快的优点,所以通常音频,视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <pthread.h>
//网口1的UDP服务器、客户端代码
void init_udp1()
{
pthread_attr_t attr1={0};
pthread_attr_init(&attr1);
pthread_attr_setdetachstate(&attr1,PTHREAD_CREATE_DETACHED);
if(pthread_create(&udp1_thread_id,NULL,udp1_server_process,0) !=0 )
{
printf("UserTimer thread (create) error\n");
//while(1);
}
printf("\n=======udp1 server thread ok======\n");
pthread_attr_t attr1_c={0};
pthread_attr_init(&attr1_c);
pthread_attr_setdetachstate(&attr1_c,PTHREAD_CREATE_DETACHED);
if(pthread_create(&udp1_c_thread_id,NULL,udp1_client_process,0) !=0 )
{
printf("UserTimer thread (create) error\n");
//while(1);
}
printf("\n=======udp1 client thread ok======\n");
}
//网口2的UDP服务器、客户端代码
void init_udp2()
{
pthread_attr_t attr2={0};
pthread_attr_init(&attr2);
pthread_attr_setdetachstate(&attr2,PTHREAD_CREATE_DETACHED);
if(pthread_create(&udp2_thread_id,NULL,udp2_server_process,0) !=0 )
{
printf("UserTimer thread (create) error\n");
}
pthread_attr_t attr2_c={0};
pthread_attr_init(&attr2_c);
pthread_attr_setdetachstate(&attr2_c,PTHREAD_CREATE_DETACHED);
if(pthread_create(&udp2_c_thread_id,NULL,udp2_client_process,0) !=0 )
{
printf("UserTimer thread (create) error\n");
}
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("Usage:./select_server [ip] [port]\n");
//return 1;
}
init_udp1();
init_udp2();
while(1)
{
;
}
printf("\n=======exit======\n");
return 0;
}
(1)网口2 服务器 代码
//该服务器仍是属于回显服务器,接收到什么就回复什么
void *udp2_server_process(void *arg)
{
struct sockaddr_in server;
struct sockaddr_in client;
char buf[BUFFER_SIZE];
int32_t len = sizeof(client);
int32_t ret = 0;
int32_t file_len = 0;
printf("\n=======udp2 server thread ok======\n");
int32_t sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("create socket failed!");
exit(1);
}
bzero(&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(udp2_port); /*本机IP-端口 主机字节序转化成网络字节序 */
server.sin_addr.s_addr = inet_addr(udp2_ip); /*本机IP 字符串转换in_addr的函数 */
/* 绑定服务器 */
if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("bind failed!");
exit(1);
}
while(1)
{
ret = recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&client, &len);
if (ret < 0)
{
perror("recvfrom failed!");
exit(1);
}
buf[ret] = '\0';
printf("client message: %s\n", buf);
printf("client's ip is %s, port is %d.\n", inet_ntoa(client.sin_addr), htons(client.sin_port));
/**< 向客户端发送信息 */
if (sendto(sockfd, "Welcome to server", BUFFER_SIZE, 0, (struct sockaddr *)&client, len) < 0)
{
perror("send file len to client error");
}
}
}
(2)网口2的客户端代码
void *udp2_client_process(void *arg)
{
struct sockaddr_in server;
struct sockaddr_in peer;
char buf[BUFFER_SIZE];
int32_t len = sizeof(peer);
char sendbuf[BUFFER_SIZE] = "welcome to client";
int32_t num = 0;
printf("\n=======udp2 client thread ok======\n");
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("create udp2 socket failed!");
exit(1);
}
bzero(&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(udp2_r_port); //远端服务器的端口
server.sin_addr.s_addr = inet_addr(udp2_r_ip);//远端服务器的地址
while(1)
{
if (sendto(sockfd, sendbuf, BUFFER_SIZE, 0, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("sendto socket failed!");
exit(1);
}
if ((num = recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&peer, &len)) < 0)
{
perror("recvfrom socket failed!");
exit(1);
}
buf[num] = '\0';
printf("Server Message: %s\n", buf);
}
close(sockfd);
return 0;
}
Makefile
.SUFFIXES : .x .o .c .s
CC := arm-linux-gcc
STRIP := arm-linux-strip
TARGET = udp_app
SRCS := main.c udp.c
LIBS = -lpthread -lc -lgcc
all:
$(CC) -static $(SRCS) -o $(TARGET) $(LIBS)
$(STRIP) $(TARGET)
cp -va $(TARGET) /nfsroot/
clean:
rm -f *.o
rm -f *.x
rm -f *.flat
rm -f *.map
rm -f temp
rm -f *.img
rm -f $(TARGET)
rm -f *.gdb
测试结果
怎么看UDP比tcp更简单,不过TCP也一定不难!!!
注意:因为服务器需要在ubuntu上运行,故采用电脑的编译器编译:编译指令gcc -o server server.c
而客户端再开发板(arm内核)上运行,故采用arm的编译器:编译指令 arm-none-linux-gnueabi-gcc -o client client.c 编译 client.c