您的位置:首页 > 理论基础 > 计算机网络

Linux Socket网络编程 分别使用TCP协议 和 UDP协议

2016-12-23 14:19 811 查看

网络模型:



Socket编程模型:



Socket的实质就是一个接口, 利用该接口,用户在使用不同的网络协议时,操作函数得以统一。而针对不同协议的差异性操作,则交给了socket去自行解决。

创建Socket套接字

函数原形:

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

函数功能: 创建socket套接字

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回套接字的标识符;若失败,返回-1.

参数说明:

domain:指明了网络域,AF_INET,用于ipv4;AF_INET6,用于ipv6。

type:协议类型,SOCK_STREAM

上面两个参数填写如下:

如:UDP协议就是AF_INET 和 SOCK_DGRAM

如:TCP协议就是AF_INET 和 SOCK_STREAM

protocol: UDP协议和TCP协议这里可以为0.

绑定地址

函数原形:

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

函数功能: 为套接字绑定相应的IP地址

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回0;若失败,返回-1.

参数说明:

sockfd:指定的socket套接字的ID

*addr:要绑定的地址,这是bind函数用到的地址类型,通用类型,第一个成员,协议族的类型,第二个成员,具体的协议值



addrlen:地址的长度,addr指向内容的长度

注意:

上述地址结构体由于不方便赋值,因此在编程中不采用,使用下面的结构体:

struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;//2字节
struct in_addr sin_addr;//4字节
unsigned char sin_zero[8];//8字节
};
struct in_addr
{
unsigned long s_addr;//此处为整形的IP地址
};


两种结构中地址数据都为 14Bytes,该结构体专用于IPV4,且需要加上头文件:

#include <netinet/in.h>


设置监听端口

函数原形:

int listen(int sockfd, int backlog);

函数功能: 监听网络端口

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回0;若失败,返回-1.

参数说明:

sockfd:指定监听其他端口的socket标识符ID(它去监听别人)

backlog:客户机的数目,允许多少个连接

等待连接函数

函数原形:

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

函数功能:

等待客户机连接,若无连接,服务器阻塞,有链接,继续运行

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回连接到的套接字的标识符;若失败,返回-1.

参数说明:

sockfd:正在监听的socket标识符,这是原始套接字ID,函数返回的是一个新的套接字ID,新ID连接到客户机,接收客户机传来的数据用新ID,原始ID可以接收其他连接请求。

*addr:客户机的地址

*addrlen:客户机的地址的长度,特别注意这里地址长度为指针

发送数据

函数原形:

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

函数功能: 从套接字上发送数据

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回发送的字节数;若失败,返回-1.

参数说明:

sockfd:发送数据的套接字ID,就是数据来源方的socket套接字的ID

*buf:发送数据存在的位置

len:发送数据的长度

flags:标志,可以直接设为0

接收数据

函数原形:

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

函数功能:

从一个socket套接字接收数据

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回接收到的字节数目;若失败,返回-1.

参数说明:

sockfd:连接成功后accept函数产生的新的套接字ID,数据来源方的ID。

*buf:接收数据存放的位置

len:接收数据的长度

flags:标志

关闭连接

close 与关闭文件函数相同用法

连接服务器

函数原形:

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

函数功能: 工作在客户机上,连接服务器

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回0;若失败,返回-1.

参数说明:

sockfd:客户机的套接字

*addr:服务器的地址

addrlen:服务器的地址的长度

UDP发送数据

函数原形:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

函数功能:

从套接字上发送数据,用于UDP协议中

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回发送的字节数;若失败,返回-1.

参数说明:

sockfd:发送数据的套接字ID描述符

*buf:发送数据存在的位置

len:发送数据的长度

flags:标志,可以直接设为0

*dest_addr:目的IP地址

addrlen:目的地址长度

UDP接收数据

函数原形:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

函数功能:

从一个socket套接字接收数据,用于UDP协议

所属头文件:

#include <sys/types.h>
#include <sys/socket.h>


返回值:

若成功,返回接收到的字节数目;若失败,返回-1.

参数说明:

sockfd:连接成功后accept函数产生的新的套接字ID

*buf:接收数据存放的位置

len:希望接收数据的长度

flags:标志

*src_addr:保存数据的来源方的IP地址

*addrlen:保存来源地址的长度,注意这里是指针

TCP协议编程模型:



通信之前首先要连接,没连接就等待。



UDP协议编程模型:



若服务器没有接收到数据,就一直等待。



IP地址格式的转换:

in_addr_t inet_addr(const char *cp)

功能:将字符串形式的IP地址转化为整数型的IP地址,整形的地址已经是网络字节序

范例: in_addr.saddr = inet_addr(“192.168.1.1”);

char *inet_ntoa (struct in_addr)

功能:将整数形式的IP地址转化为字符串形式的IP地址

网络字节序的转换:

网络通信中数据的发送和接收统一规定为大端模式,即:高位数据存放在低位地址处。因此只要数据大于等于2字节,都要进行如下转换:

- 发端: 从本机字节序转换为网络字节序

- 收端: 从网络字节序转换为本机字节序

uint32_t htonl(uint32_t hostlong);

将32位的数据从主机字节序转换为网络字节序

in_addr.saddr = htonl(INADDR_ANY);

//绑定所有的IP地址

uint16_t htons(uint16_t hostshort);

将16位的数据从主机字节序转换为网络字节序

uint32_t ntohl(uint32_t netlong);

将32位的数据从网络字节序转换为主机字节序

uint16_t ntohs(uint16_t netshort);

将16位的数据从网络字节序转换为主机字节序

例程:

1、TCP服务器,采用多进程实现并发。

/* 实现并发处理服务器,使用多进程 */
/* TCP协议中服务器的程序 */
#include <sys/types.h> #include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>

#define portnum 3333

void main()
{
int pid;//进程ID
int socket_server1;//服务器套接字标识符
int socket_server2;//服务器连接之后的套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int size_addr;
char receive_buffer[128];//接收到的数据存放的位置
int receive_nbytes;//接收到的字节数目

//创建socket 套接字
socket_server1 = socket(AF_INET, SOCK_STREAM, 0);
if (socket_server1 == -1)//失败就退出
{
printf("creat socket of server fail.\n");
exit(1);
}

//设置要绑定的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定所有的IP地址,转换为网络字节序

//绑定地址
bind(socket_server1, (struct sockaddr *) (&server_addr),
sizeof(struct sockaddr));

//监听端口
listen(socket_server1, 5);

size_addr = sizeof(struct sockaddr);
while(1)
{
//等待连接
socket_server2 = accept(socket_server1,
(struct sockaddr *) (&client_addr),
(socklen_t *) (&size_addr));
//特别注意这里地址长度为指针
printf("server get connection from %s\n",
inet_ntoa(client_addr.sin_addr));

//一旦实现了连接,就创建一个子进程
pid = fork();
if (pid == 0)
{
//接收客户机数据
receive_nbytes = recv(socket_server2, receive_buffer, 128, 0);
receive_buffer[receive_nbytes] = '\0';//添加结束符
printf("server received %s\n", receive_buffer);

close(pid);
//结束新建立的连接
close(socket_server2);
exit(0);
}
}
//结束原始的连接
close(socket_server1);
}


2、TCP客户端

/* TCP协议中客户机的程序 */
#include <sys/types.h> #include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>

#define portnum 3333

void main()
{
int socket_client;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int connect_tmp;//判断连接是否成功
char buffer[128];//发送的数据存放的位置
int nbytes;//发送的字节数目

//创建socket 套接字
socket_client = socket(AF_INET, SOCK_STREAM, 0);
if (socket_client == -1)//失败就退出
{
printf("creat socket of client fail.\n");
exit(1);
}

//设置要连接的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = inet_addr("192.168.234.129");
//指定IP地址,转换为整数类型的地址,已经是网络字节序

//连接服务器
connect(socket_client, (struct sockaddr *)(&server_addr),
sizeof(struct sockaddr));
if (connect_tmp == -1)
{
printf("connect fail.\n");
exit(1);
}

//发送数据
printf("Please input char.\n");
fgets(buffer, 128, stdin);
send(socket_client, buffer, strlen(buffer), 0);

//结束连接
close(socket_client);
}


3、UDP服务器

/* UDP协议中服务器的程序 */
#include <sys/types.h> #include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>

#define portnum 3333 //定义端口号

void main()
{
int socket_server;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int size_addr;//记录地址的长度
char receive_buffer[128];//接收到的数据存放的位置
int receive_nbytes;//接收到的字节数目

//创建socket 套接字
socket_server = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_server == -1)//失败就退出
{
printf("creat socket of server fail.\n");
exit(1);
}

//设置要绑定的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定所有的IP地址,转换为网络字节序

//绑定地址
bind(socket_server, (struct sockaddr *) (&server_addr),
sizeof(struct sockaddr));

size_addr = sizeof(struct sockaddr *);//地址长度
while(1)
{
//接收客户机数据
bzero(receive_buffer, sizeof(receive_buffer));
receive_nbytes = recvfrom(socket_server, receive_buffer, 128, 0,
(struct sockaddr *)(&client_addr), (socklen_t *)(&size_addr));
receive_buffer[receive_nbytes] = '\0';//添加结束符
printf("server received %s\n", receive_buffer);
}

//结束连接
close(socket_server);
}


4、UDP客户端

/* UDP协议中客户机的程序 */
#include <sys/types.h> #include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>

#define portnum 3333 //定义端口号

void main(int argc, char **argv)
{
int socket_client;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
char buffer[128];//发送的数据存放的位置
int nbytes;//发送的字节数目

//创建socket 套接字
socket_client = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_client == -1)//失败就退出
{
printf("creat socket of client fail.\n");
exit(1);
}

//设置要发送数据的的目的地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//输入IP地址
//指定IP地址,转换为整数类型的地址,已经是网络字节序

while(1)
{
bzero(buffer, 128);
//发送数据
printf("Please input char.\n");
fgets(buffer, 128, stdin);
sendto(socket_client, buffer, strlen(buffer), 0,
(struct sockaddr *)(&server_addr), sizeof(server_addr));
}

//结束连接
close(socket_client);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息