struct sockaddr_in
{ short int sin_family
;
unsigned short int sin_port
;
struct in_addr sin_addr
;
unsigned char sin_zero
[8];
};
struct sockaddr
{
unsigned short sa_family
;
char sa_data
[14]; };
主机字节序变为网络字节序 • uint16_t htons(uint16_t hostshort) • uint32_t htonl(uint32_t hostlong) • 网络字节序变为主机字节序 • uint16_t ntohs(uint16_t netshort) • uint32_t ntohl(uint16_t netlong)
INADDR_ANY 转换过来就是0.0.0.0,泛指本机的意思
简单UDP循环服务器socket编程基本步骤
简单TCP循环服务器socket编程基本步骤
UDP编程函数解析
socket()
创建套接字函数<sys/socket.h>
格式: int socket(intdomain,inttype,int protocol)
功能:根据给定的参数创建一个套接字描述子,成功返回一个 套接字描述符,不成功返回-1。
int socket(int domain, int type,int protocol)
参数: domain指示网络协议族,PF_INET或PF_INET6标识TCP/IP协议 type指示协议类型,如SOCK_STREAM(TCP)SOCK_DGRAM(UDP)和SOCK_RAW protocol指示特定的协议类型,type为SOCK_STREAM和SOCK_DGRAM时 protocol设置为0,type为SOCK_RAW时可设置不同的值(IPPROTO_ICMP、 IPPROTO_IGMP、 IPPROTO_RAW等)
bind()
绑定套接字函数 <sys/socket.h>
格式: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)功能:绑定IP地址和端口号到套接字sockfd。成功返回0,否则返回-1。参数: sockfd:指示套接字描述符; addr:指示本地的套接字地址; addrlen:指示套接字地址长度;
accept()
接受请求套接字函数<sys/socket.h>
格式: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)功能:接收连接请求,成功返回一个非负的整数作为连接套接字描述符; 否则返回-1。使用该函数后,系统从完成队列中取出socket,为每个连接 请求创建一个新的连接套接字,服务器只对新的连接使用该套接字,原来 的监听套接字接受其他的连接请求。参数: sockfd:指示套接字描述符; addr:获得对方的套接字地址(作为返回结果); addrlen:指示套接字地址长度(作为返回结果) ;
recv()
接收数据套接字函数
格式: ssize_t recv(int sockfd, void *buf, size_t len, int flags)功能:接收指定套接字的数据。成功返回接收的字节数,否则返回-1。 tcp协议收到FIN数据,返回0;参数: sockfd指示套接字描述符; buf用于存放接收数据的缓冲区; len指示接收缓冲区的长度; flags指示接收数据的属性; flags是传输控制标志,其值定义如下: 0:常规操作,如同read()函数; MSG_PEEK:只查看数据而不读出数据, 后续读操作仍然能读该数据; MSG_OOB:忽略常规数据,而只读带 外数据; MSG_WAITALL:recv函数只有在将接收 缓冲区填满后才返回。
recvfrom()
格式: ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)功能:接收指定套接字的数据。成功返回接收的字节数,否则返回-1。 tcp协议收到FIN数据,返回0;参数: sockfd指示套接字描述符; buf:用于存放接收数据的缓冲区; len:指示接收缓冲区的长度; flags:指示接收数据的属性,同recv(); src_addr和addrlen:分别指示接收数据的来源地址及其地址长度。
send()
发送数据套接字函数
格式: int send(int sockfd, const void *buf, size_t len, int flags)功能:通过已经连接的套接字发送数据。成功返回发送的字符数, 否则返回-1。参数: sockfd:指示套接字描述符; buf:要发送的数据缓冲区; len:指示要发送数据的长度; flags:指示发送数据的属性,通常设置为0。其它典型的 MSG_OOB指示带外数据发送; flags是传输控制标志,其值定义如下: 0:常规操作,如同write()函数; MSG_OOB:发送带外数据; MSG_DONTROUTE:忽略底层路 由协议,直接发送。
sendto()
格式: int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)功能:向指定的目的地址发送数据。成功返回发送的字符数,否则 返回-1。参数: sockfd:指示套接字描述符; buf:要发送的数据缓冲区; len:指示要发送数据的长度; flags:指示发送数据的属性,典型的MSG_OOB指示带外数据发送; dest_addr和addrlen:分别指示目的地址和目的地址的长度。
close()
关闭套接字函数 <unistd.h>
格式: int close(int sockfd)功能:关闭网络套接字。成功返回0,否则返回-1。参数: sockfd指示套接字描述符;
close函数缺省功能是将套接字做上“已关闭”标记,并立即返回到进程。这 个套接字不能再为该进程所用。 • 正常情况下,close将引发向TCP的四次挥手终止序列,但在终止前将发送已 排队的数据; • 如果套接字描述符访问计数在调用close后大于0(在多个进程共享同一个套接 字的情况下),则不会引发TCP终止序列(即不会发送FIN分节);
UNIX中UDP编程实例
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_LENGTH 1024
int main(int argc
, char*argv
[]) {
struct sockaddr_in server_addr
, client_addr
;
int sockfd
;
socklen_t client_addr_len
= sizeof(struct sockaddr
);
char buf
[BUFFER_LENGTH
];
int n
;
if (argc
!= 3){
fputs("usage: ./udpserver serverIP serverPort\n", stderr);
exit(1);
}
sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
if (sockfd
== -1){
perror("created udp socket");
exit(1);
}
bzero(&server_addr
, sizeof(server_addr
));
server_addr
.sin_family
= AF_INET
;
server_addr
.sin_port
= htons(atoi(argv
[2]));
if (inet_aton(argv
[1], &server_addr
.sin_addr
) == 0){
perror("inet_aton");
exit(1);
}
if (bind(sockfd
, (struct sockaddr
*)&server_addr
,sizeof(server_addr
)) == -1){
perror("call to bind");
exit(1);
}
while(1){
memset(buf
, 0, BUFFER_LENGTH
);
n
= recvfrom(sockfd
, buf
, BUFFER_LENGTH
, 0, (struct sockaddr
*)&client_addr
, &client_addr_len
);
if (n
== -1){
perror(“fail to receive”
);
exit(1);
} else{
buf
[n
] = '\0';
printf("%s\n", buf
);
gets(buf
);
n
= sendto(sockfd
, buf
, strlen(buf
), 0, (struct sockaddr
*)&client_addr
, sizeof(client_addr
));
if (n
== -1){
perror("fail to reply");
exit(1);
}
}
}
if (close(sockfd
) == -1){
perror("fail to close");
exit(1);
}
puts("UDP Server is closed!\n");
return EXIT_SUCCESS
;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define BUFFER_LENGTH 1024
int main(int argc
, char *argv
[]) {
struct sockaddr_in server_addr
;
int sockfd
;
socklen_t server_addr_len
= sizeof(struct sockaddr
);
char buf
[BUFFER_LENGTH
];
int n
;
if (argc
!= 3){
fputs("usage: ./udpclient serverIP serverPort\n", stderr);
exit(1);
}
sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
if (sockfd
== -1){
perror("created udp socket...\n");
exit(1);
}
bzero(&server_addr
, sizeof(server_addr
));
server_addr
.sin_family
= AF_INET
;
server_addr
.sin_port
= htons(atoi(argv
[2]));
if (inet_aton(argv
[1], &server_addr
.sin_addr
) == 0){
perror("inet_aton");
exit(1);
}
memset(buf
, 0, BUFFER_LENGTH
);
gets(buf
);
n
= sendto(sockfd
, buf
, strlen(buf
), 0, (structsockaddr
*)&server_addr
, sizeof(server_addr
));
if (n
== -1){
perror("fail to send");
exit(1);
}
memset(buf
, 0, BUFFER_LENGTH
);
n
= recvfrom(sockfd
, buf
, BUFFER_LENGTH
, 0, (struct sockaddr
*)&server_addr
, &server_addr_len
);
if (n
== -1){
perror("fail to receive");
exit(1);
} else{
buf
[n
] = '\0';
printf("Server: %s\n", buf
);
}
if (close(sockfd
) == -1){
perror("fail to close");
exit(1);
}
puts("UDP Client is closed!\n");
return EXIT_SUCCESS
;
}
多进程方式套接字编程
fork()
每有一个客户端发来链接请求,利用fork()形式创建子进程,该子进程来处理这个客户端
关于fork()函数的内容可以参见该文章https://blog.csdn.net/qq_43589143/article/details/106717493
代码实例
int main(void) {
int listenfd
, connfd
;
pid_t pid
;
int BACKLOG
= 5;
if ((listenfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
perror(“Create socket failed
.”
);
exit(1);
}
bind(listenfd
, …
);
listen(listenfd
, BACKLOG
);
while(1) {
if ((connfd
= accept(sockfd
, NULL, NULL)) == -1) {
perror(“Accept error
.”
);
exit(1);
}
if ((pid
= fork()) >0) {
close(connfd
);
……
conntinue
;
}
else if (pid
== 0) {
close(listenfd
);
……
close(connfd
);
exit(0);
} else {
perror(“Create child process failed
.”
);
exit(1);
}
}
}
fork()的问题
Fork是“昂贵的”-费时费资源;父子进程间通信比较麻烦;并发进程的数量是有限的;
多线程方式套接字编程
使用<pthread.h> 库函数 具体使用参见该文章https://blog.csdn.net/qq_43589143/article/details/106699210
编程实例
#define PORT 1234
#define BACKLOG 2
#define MAXDATASIZE 1000
void process_cli(int connectfd
, sockaddr_in client
);
void *start_routine(void *arg
);
struct ARG
{
int connfd
;
struct sockaddr_in client
;
};
int main(void) {
int listenfd
, connectfd
;
pthread_t tid
;
struct ARG
*arg
;
struct sockaddr_in server
, client
;
int sin_size
;
…
…
…
sin_size
= sizeof(struct sockaddr_in
);
while(1) {
if ((connectfd
= accept(listenfd
, (structsockaddr
*)&client
, &sin_size
)) == -1)
arg
= new ARG
;
arg
-> connfd
= connectfd
;
memcpy((void *)&arg
-> client
, &client
, sizeof(client
));
if (pthread_create(&tid
, NULL, start_routine
, (void *)arg
)
}
close(listendfd
);
}
void process_cli(int connectfd
, sockaddr_in client
) {
…
}
void *start_routine(void *arg
) {
struct ARG
*info
;
info
= (ARG
*)arg
;
process_cli(info
-> connfd
, info
-> client
);
delete arg
;
pthread_exit(NULL);
}