一.TCP-----服务器端
在TCP编程中,client端涉及以下五个过程:
- socket
- bind 绑定
- listen 监听
- accept 等待连接
- send/recv 收发数据
1. 首先使用socket()函数创建一个套接字,在socket()函数中不涉及,任何三大元素(源,端口,地址)
2.然后使用bind()函数,将绑定的端口和地址进行绑定。代码如下:
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0 , 8 );
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr,
sizeof(struct sockaddr));
此处我们将sin_addr 设置为 INADDR_ANY 表示可以和任何的主机通信。
3. 使用listen()函数开启数据监听,宣告服务器可以接受连接请求。
#define BACKLOG 10
iRet = listen(iSocketSever, BACKLOG);
if(iRet == -1)
{
printf("listen error!\n");
return -1;
}
BACKLOG表示最多可以监听10路连接。
4. 然后使用accept()函数,获得连接请求,并且建立连接。
5. 最后使用send()/recv()函数进行数据接收和发送。
将接收到的数据打印在终端显示处理,此处我们整一个高级的操作。当有一个新的链路连接时,我们就使用fork()函数创建子进程,我们就可以实现打开多个进程。代码如下:
while(1){
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketSever, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if(iSocketClient != -1)
{
++iClientNum;
printf("Get connect From client %d : %s\n",iClientNum , inet_ntoa(tSocketClientAddr.sin_addr));
/*执行到fork,会复制子进程*/
/*子执行fork()==0,父进程执行fork()!=0内的语句*/
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);
}
}
}
/*父进程*/
//else{}
}
}
二.TCP-----client端
在TCP编程中,client端涉及以下三个过程:
- socket
- connect 建立连接
- send/recv 收发数据
1.首先使用socket()函数创建一个套接字
2.然后使用connect()函数建立一个连接,在 connect 中所指定的地址是想与之通信的服务器的地址。
我们使用结构体sockaddr_in封装端口,地址等。
#define SERVER_PORT 8888 //绑定的端口
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
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));
在给sin_port赋值时要使用htons()函数将端口转换为网络字节序。htons全称host to net shorted。与此同时,由于我没有将程序的目标地址写死,所以还需要将运行程序时以字符串形式输入的IP地址,使用inet_aton()函数转换为二进制形式的 IPv4 地址。
3.最后在while循环中通过按键输入,传输数据。
while(1)
{
if(fgets(ucSendBuf, 999 , stdin))
{
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if(iSendLen <= 0)
{
printf("Send error!\n");
}
}
}
三. TCP编程总结流程图
四. UDP----服务器端
相较于TCP传输,服务器端不需要监听和等待连接。只需要将TCP编程中与listen()和accept()相关的删除即可。只需要将接受和发送的代码进行修改。如下:
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);
}
}
五. UDP----client端
相较于TCP传输,UDP的客户端传输更加简单,可以只需要一个socket()函数,然后进行发送和接收,也可以进行connect()后,再进行数据传输。但是connect()实则是一个虚假的连接,只是将目标地址和端口封装。使用sendto()和recvfrom()函数中已经封装了目标地址和端口。
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
iAddrLen = sizeof(struct sockaddr);
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
(const struct sockaddr *)&tSocketServerAddr, iAddrLen);
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
六. UDP编程总结流程图
七. TCP附录代码
1. server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <string.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 //监听10路连接
int main(int argc, char **argv)
{
int iSocketSever;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;//客户端地址信息
int iSocketClient;
int iRet;
int iAddrLen;//目标地址长度
unsigned char ucRecvBuf[1000];//数据接收缓冲区
int iRecvLen ;//接收数据的长度
int iClientNum = -1;//查看是来自哪个客户端
signal(SIGCHLD, SIG_IGN);
iSocketSever = socket(AF_INET, SOCK_STREAM , 0);
if( iSocketSever == -1)
{
printf("socket error!\n");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);//host to net shorted 将端口8888转换为网络字节序
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0 , 8 );
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr,
sizeof(struct sockaddr));
if(iRet == -1)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketSever, BACKLOG);
if(iRet == -1)
{
printf("listen error!\n");
return -1;
}
while(1){
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketSever, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if(iSocketClient != -1)
{
++iClientNum;
printf("Get connect From client %d : %s\n",iClientNum , inet_ntoa(tSocketClientAddr.sin_addr));
/*执行到fork,会复制子进程*/
/*子执行fork()==0,父进程执行fork()!=0内的语句*/
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);
}
}
}
/*父进程*/
//else{}
}
}
close(iSocketSever);
return 0;
}
2. client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/*socket
*connect 建立连接
*send/recv 收发数据
*/
#define SERVER_PORT 8888 //绑定的端口
#define BACKLOG 10 //监听10路连接
int main(int argc, char **argv)
{
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iRet;
unsigned char ucSendBuf[1000];//发送缓冲区、
int iSendLen;//发送数据的长度
if(argc != 2)
{
printf("Usager : %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 shorted 将端口8888转换为网络字节序
//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)
{
printf("Send error!\n");
}
}
}
return 0;
}
tips:关于各个函数的使用和相关作用,以及参数可以参考Linux---网络通信相关函数。