入门知识查看https://blog.csdn.net/qq_31505483/article/details/73929798
网络编程本质上也是数据传输,也满足数据传输三要素:源、目的、长度。服务器只能被动地响应请求,而客户端可以主动发出请求。
网络通信有两种协议:TCP和UDP,其中,TCP是可靠的(“三次握手”的验证方法,可参考博客中的讲解),有一定的重传机制。UDP没有三次握手机制,数据发出后就不再进行验证,不关心对方有没有收到。并不一定所有场合下都适合使用可靠地TCP,比如在视频传输过程中,由于TCP的重传机制,如果某一帧对方接收不成功,就会一直重传,后面的图像就会被滞后,那么视频播放就会很卡,而使用UDP就不会有这种问题。对于一些控制命令,就比较适合用TCP。
下图中的A和B不是指两台设备,而是指两个程序,这两个程序可以在同一台电脑上,也可以在不同设备上。
TCP传输程序编写方法:
使用TCP协议进行数据传输怎么写程序呢?如下图所示。Socket函数所返回的fd文件描述符,服务器与客户端都可以通过对这个文件的读写操作来完成数据传输,也就是说可以把fb理解为一个通道,服务器与客户端将数据放在上面或者从上面把数据读取出来。由于服务器只能被动地响应请求,因此要不断地检测某个端口,才能知道有数据传输进来,因此要使用bind函数将自己的IP、端口和fd绑定(IP和端口的关系可参照https://blog.csdn.net/ios_xumin/article/details/50502720)。
前面提到过,网络编程也是一种数据传输,也需指定数据传输的三要素:源、目的、长度,源和目的就是在bind和connect函数中指定的,长度是在send或write函数,recv或read函数中指定的。
以下程序中的TCP相关的函数的参数都可以参照博客。
TCP传输程序示例:
Server.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
//使用man socket可以查看socket函数所需要包含的头文件
/* 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); //在销毁子进程后,会产生僵尸进程,需要父进程来清理,换句话说,父进程要为子进程收尸,该函数就是这个作用。子进程退出后会给父进程发送一个信号,这个信号的处理函数就是SIG_IGN。
iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iSocketServer)
{
printf("socket error!\n");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */ //转换为网络字节序,这一个函数在博客里也有讲明。这个参数指明要检测的端口
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //指定要检测的IP,INADDR_ANY这个参数表示所有IP
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); //一台电脑上可能会有多个IP,多个端口,检测哪一个IP,哪一个端口,可以在这里指定。
if (-1 == iRet)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketServer, BACKLOG);
if (-1 == iRet)
{
printf("listen error!\n");
return -1;
}
while (1)
{
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen); //接收谁的连接呢?客户端的IP等信息就会保存在tSocketClientAddr结构体里面,返回客户端的socket。
if (-1 != iSocketClient)
{
iClientNum++;
printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr)); //连接成功后打印客户端的IP地址,但是需要将sin_addr转换为我们常见的192.168….这样的字符串打印
if (!fork()) //有多个客户端时,每来一个连接,就创建一个进程。子进程的代码和父进程完全一样,但是子进程是直接从fork()处开始往下跑的。
{
/* 子进程的源码 */
while (1)
{
/* 接收客户端发来的数据并显示出来 */
iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);//从iSocketClient接收999长度的数据,存到ucRecvBuf里面
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;
}
插讲fork()创建子进程:对于程序中的if(0==fork()) {} else {}结构(子进程创建成功),主进程会执行else分支,子进程和父进程代码完全一样,但是会从for()处直接开始执行,如果子进程创建成功,会执行if分支。
Client.c:
#include
#include
#include
#include
#include
#include
#include
#include
/* 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
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 (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)) //通过参数[1]传入服务器的地址信息
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); //请求建立到服务器的连接,tSocketServerAddr存有服务器的地址信息
if (-1 == iRet)
{
printf("connect error!\n");
return -1;
}
while (1)
{
if (fgets(ucSendBuf, 999, stdin)) //从标准输入那里获取要传输的数据,如下图所示,保存到ucSendBuf
{
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
UDP传输程序示例:
UDP传输涉及到的系统调用如下图所示。其中,用send发送数据时,connect函数必须使用,使用sendto发送数据时,connect函数可以不用。
Server.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* socket
* bind
* sendto/recvfrom
*/
#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!\n");
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 (-1 == iRet)
{
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
#include
#include
#include
#include
#include
#include
#include
/* 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;
int iAddrLen;
if (argc != 2)
{
printf("Usage:\n");
printf("%s
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
iAddrLen = sizeof(struct sockaddr);
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
(const struct sockaddr *)&tSocketServerAddr, iAddrLen);
#endif
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}