以下内容分别为TCP 与 UDP编程,内容有相似或者重合部分,可根据流程 相互对照学习,都已经附上源码。
TCP编程
TCP 服务器端_Server :
**1.**socket 创建 tcp套接字 (监听的套接字)
int iSocketServer = socket(AF_INET,SOCK_STREAM,0);
2、IPv4套接字地址结构
#include <netinet/in.h>
struct sockaddr_in{
unsigned short sin_family; //2 字节 协议AF_INET
unsigned short sin_port; //2字节 端口
struct in_addr sin_addr; //4字节 IP地址(32位无符号整数)
unsigned char sin_zero[8]; //8字节 全写0
}
struct in_addr:
struct in_addr{
in_addr_t s_addr;
}
如果使用 Internet 所以 sin_family 一般为 AF_INET。
⚫ sin_addr 设置为 INADDR_ANY 表示可以和任何的主机通信。
⚫ sin_port 是要监听的端口号。
定义IPv4地址结构,存放 本机信息
#define Server_port 8888
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_ad`dr = INADDR_ANY;可以和任何的主机通信
memset(tSocketServerAddr.sin_zero , 0, 8);
3、两种地址结构使用场合
struct sockaddr_in 是IPv4地址结构(存放客户端、服务器的地址信息(协议,port,IP))
struct sockaddr 是通用地址结构,不是存放数据,只是socket api类型转换
sockaddr定义 见下方4.bind介绍 中
4.bind给服务器的绑定固定的port、IP地址信息**
给iSocketServer套接字bind绑定一个固定的地址信息
bind 函数
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
从函数用于将地址绑定到一个套接字。
⚫ sockfd 是由 socket 函数调用返回的文件描述符。
⚫ my_addr 是一个指向 sockaddr 的指针。
⚫ addrlen 是 sockaddr 结构的长度。
⚫ bind 将本地的端口同 socket 返回的文件描述符捆绑在一起.成功是返回 0,
失败的情况和 socket 一样。
***注: ***sockaddr 的定义(通用地址结构):
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
iRet= bind(iSocketServer, (const struct sockaddr*)&tSocketServerAddr, sizeof(struct sockaddr));
**注意:**bind的函数原型的第二个参数,是通用地址结构struct sockaddr ,而我们定义结构体地址的时候,一般选择IPv4结构体地址 ,见2.ipv4套接字地址结构 ,例:struct sockaddr_in tSocketServerAddr;
所以当bind()中 ,我们想填入tSocketServerAddr的结构体地址,需要在前面加入强制类型转换(const struct sockaddr*) 将其转换为通用地址结构struct sockaddr。
3、两种地址结构使用场合
struct sockaddr_in 是IPv4地址结构(存放客户端、服务器的地址信息(协议,port,IP))
struct sockaddr 是通用地址结构,不是存放数据,只是socket api类型转换
3.listen监听 并且创建连接队列
listen 监听 :等待客户端的连接到来,经过三次握手(底层自动执行),将客户端放入连接队列
#define BACKLOG 10
iRet = listen(iSocketServer,BACKLOG);
功能:
1、将监听套接字由主动变成被动(是在server和client两端 套接字名字一样的情况下,我们这里 设定的 server端的套接字名字是iSocketServer,所以已经是被动。
2、为改套接字创建连接队列,队列长度就是BACKLOG
返回值:成功为0,失败为-1
连接队列,分成完成连接 和未完成连接的状态,分别对应,完成三次握手/第1、 2次握手的状态
*4.accep 提取客户端的链接
此内容放在while(1)循环里面,表示持续等待客户端连接
iAddrLen=sizeof(struct sockaddr);
iSocketClient= accept(iSocketServer, ( struct sockaddr *)&tSocketClientAddr, &iAddrLen); //强转是因为socket适用于ipv4、ipv6,通用地址结构强转?
连接成功client的话,打印client的信息
printf("get connect from client %d: %s\n",iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
iClientNum表示 连接客户端的个数 ,然后inet_ntoa(tSocketClientAddr.sin_addr)是客户端的ip地址 (192.168.xx.xx)要打印出来
然后调用fork()函数对应多个连接的客户端开启多个子进程
5.(send发送) recv 接收客户端的消息**
tcp中recv的注意点:
int len =recv(已连接套接字(注意不是监听套接字),buf,sizeof(buf),0);
len 表示recv 收到的实际字节数
如果len为0 ,表示客户端已经断开连接,-1表示 读取数据出错
tcp中send的注意点(C代码放在了client里):
int iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
如果返回值iSendLen >0表示发送成功 ,即实际发送的字节数
如果 为-1 表示发送失败
注意:不能发送0长度报文 ,因为recv收到0长度 会断开连接 (但是udp允许)
iRecvLen = recv(iSocketClient,ucRecvBuf,999 ,0) ;
{
ucRecvBuf[iRecvLen]='\0'; //收到的消息加结束符
printf("Get Msg from Client %d:%s\n",iClientNum,ucRecvBuf);//打印从which客户端发送的消息
}
6.close关闭所有套接字
源码阅读
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* listen
* accept
* send/recv
*/
#define Server_port 8888
#define BACKLOG 10
int main(int argc,char**argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
signal(SIGCHLD,SIG_IGN);
iSocketServer = socket(AF_INET,SOCK_STREAM,0);
if(-1==iSocketServer)
{
printf("socket error!");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0, 8);
iRet= bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if(iRet==-1)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketServer,BACKLOG);
if(iRet==-1)
{
printf("listen error!\n");
return -1;
}
while (1)
{
iAddrLen=sizeof(struct sockaddr);
iSocketClient= accept(iSocketServer, ( struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if(iSocketClient!=-1)
{
iClientNum++;
printf("get connect from client %d: %s\n",iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
if(!fork())
{//子进程的源码
while(1)
{//接收客户端发来的数据并显示出来
iRecvLen = recv(iSocketClient,ucRecvBuf,999 ,0) ;
if (iRecvLen<=0)
{
close(iSocketClient);
return -1;
}
else
{
ucRecvBuf[iRecvLen]='\0';
printf("Get Msg from Client %d: %s\n",iClientNum,ucRecvBuf);
}
}
}
}
}
close(iSocketServer);
return 0;
}
TCP 客户端_Client :
1.创建套接字
int iSocketClient=socket(AF_INET, SOCK_STREAM, 0);
2.connect
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if(inet_aton(argv[1],&tSocketServerAddr.sin_addr)==0)//输入ip成功
/*
int inet_aton(const char *cp,struct in_addr *inp)
函数里面 a 代表 ascii,n 代表network. 第一个函数表示将a.b.c.d的IP转换为32位的IP,存储在 inp指针里面.
第二个是将32位IP转换为a.b.c.d的格式.
*/
{
printf("invalid server_ip\n");
return -1;
}
//argv[1] 就是我们运行程序时候 ./client <ip>的 <ip>
memset(tSocketServerAddr.sin_zero, 0, 8);
上面是常规的对服务器端的连接信息的信息声明
连接服务器
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr ,sizeof(struct sockaddr));
iSocketClient :发起连接的 套接字
tSocketServerAddr:服务器的地址 , sizeof(struct sockaddr):服务器的地址长度
返回值:成功为0,失败为-1
如果客户端和服务器通信,必须使用connect 事先建立连接
连接成功后,使用while(1)进入循环准备发消息
3.send发送请求 (recv接收应答)
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
参数:
iSocketClient:客户端的套接字
ucSendBuf:发送的消息
strlen(ucSendBuf):信息的长度
flags ==0
返回值:成功返回发送的字节数,失败返回-1 ,不可以发送0字节的
4.close关闭套接字
源码阅读:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* connect
* send/recv
*/
int main(int argc ,char**argv)
{
int iSocketClient ;
struct sockaddr_in tSocketServerAddr ;
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
if (argc!=2)
{
prinft("Usage:\n");
printf("%s<server_ip>\n",argv[0]);
return -1;
}
iSocketClient=socket(AF_INET, SOCK_STREAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if(inet_aton(argv[1],&tSocketServerAddr.sin_addr)==0)
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr ,sizeof(struct sockaddr));
if(-1==iRet)
{
printf("connect error\n");
return -1;
}
while(1)
{
if(fgets(ucSendBuf,999,stdin))
{
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if(iSendLen<=0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
UDP编程
1、特点和应用:
UDP特点:
1.相比TCP速度快 (因为TCP要建立连接,而UDP不需要)
2.简单的请求、应答应用程序可以使用UDP
3.对于海量数据传输不应该使用UDP
4.广播和多播应用必须使用UDP(广播不需要连接)
UDP*应用:
DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体
)**
2、网络通信需要解决三大问题(应用层)
协议、端口(port)、IP地址
socket套接字是一个特殊的文件描述符,可以使用open、write、read、close进行网络通信,通过socket函数调用得到这个网络通信的文件描述符(套接字)
3.UDP的编程架构
4.socket编程的API
1、socket创建通信的套接字
int iSocketServer;
iSocketServer = socket(AF_INET,SOCK_DGRAM,0);
AF_INET 代表ipv4 , AF_INET6代表ipv6
type类型:SOCK_DGRAM(UDP的套接字)、SOCK_STREAM(TCP的套接字),SOCK_RAW(原始套接字)
protocol协议类别:(0、IPPROTO_TCP\IPPROTO_UDP)
返回值:>0 创建的文件描述符 (套接字),<0创建失败
struct sockaddr_in 是IPv4地址结构(存放客户端、服务器的地址信息(协议,port,IP))
struct sockaddr 是通用地址结构,不是存放数据,只是socket api类型转换
2、IPv4套接字地址结构
#include <netinet/in.h>
struct sockaddr_in{
unsigned short sin_family; //2 字节 协议AF_INET
unsigned short sin_port; //2字节 端口
struct in_addr sin_addr; //4字节 IP地址(32位无符号整数)
unsigned char sin_zero[8]; //8字节 全写0
}
struct in_addr:
struct in_addr{
in_addr_t s_addr;
}
如果使用 Internet 所以 sin_family 一般为 AF_INET。
⚫ sin_addr 设置为 INADDR_ANY 表示可以和任何的主机通信。
⚫ sin_port 是要监听的端口号。
⚫ bind 将本地的端口同 socket 返回的文件描述符捆绑在一起.成功是返回 0,
失败的情况和 socket 一样。
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;//可以和任何的主机通信
3、sendto()
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
⚫ sendto 和 send 相似,区别在于 sendto 允许在无连接的套接字上指定一个
目标地址。
⚫ dest_addr 表示目地机的 IP 地址和端口号信息,
⚫ addrlen 常常被赋值为 sizeof ( struct sockaddr)。
⚫ sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
iSendLen = sendto(iSocketClient, ucSendBuf , strlen(ucSendBuf) , 0, (const struct sockaddr *)&tSocketServerAddr , sizeof(struct sockaddr));
**4.recvfrom()**接收消息
如果udp不发数据前就要接收消息,必须对udp套接字进行绑定
recvfrom 函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
⚫ recvfrom 通常用于无连接套接字,因为此函数可以获得发送者的地址。
⚫socket:udp套接字,接收消息端 (服务器端)
⚫buffer:用来存放消息的空间起始地址 ,len 是buf的长度,flags = 0 即可
⚫ src_addr 是一个 struct sockaddr 类型的变量,该变量保存源机的 IP 地
址及端口号。 (发送者的ipv4地址信息)
⚫ addrlen 常置为 sizeof ( struct sockaddr)。
返回值:
成功:返回实际收到的字节数 失败:返回-1
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
成功操作打印文章来源:https://www.toymoban.com/news/detail-775842.html
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
5.源码阅读
Server.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* send/recv
*/
#define Server_port 8888
int main(int argc,char**argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
iSocketServer = socket(AF_INET,SOCK_DGRAM,0);
if(-1==iSocketServer)
{
printf("socket error!");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0, 8);
iRet= bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if(iRet==-1)
{
printf("bind error!\n");
return -1;
}
while (1)
{
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
}
close(iSocketServer);
return 0;
}
client.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/* socket
* connect
* send/recv
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
#if 0
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("connect error!\n");
return -1;
}
#endif
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
#if 0
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
#else
iSendLen = sendto(iSocketClient, ucSendBuf , strlen(ucSendBuf) , 0, (const struct sockaddr *)&tSocketServerAddr , sizeof(struct sockaddr));
#endif
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
#参考:
1.韦东山linux编程
2.千锋教育物联网文章来源地址https://www.toymoban.com/news/detail-775842.html
到了这里,关于初学记录【linux应用】 TCP/UDP 网络编程 C语言的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!