本文最后更新于:2023年4月15日 晚上
socket编程学习笔记 | 1 入门 1 | Server端 大致流程:
创建socketsocket()
->绑定ip和端口bind()
->设置监听模式listen()
->接受客户端连接accept()
->发送与接受数据send() / recv()
->关闭连接close()
创建socket: int listenfd = socket(AF_INET,SOCK_STREAM,0)
: ->失败返回-1
e.g.
1 2 3 4 5 6 7 int listenfd;if ( (listenfd = socket (AF_INET,SOCK_STREAM,0 )) == -1 ) { perror ("socket" ); return -1 ; }
AF_INET
: 协议簇, AF_INET表示Ipv4 网络协议
SOCK_STREAM
: 提供双向连续且可信赖的数据流,即 TCP
PROTOCAL:
传输协议编号, 就写0
绑定地址和端口: int bind(int sockfd, struct sockaddr * my_addr,int addrlen);
->成功返回0, 失败返回-1
其中struct sockaddr_in
对应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct sockaddr_in { unsigned short int sin_family; uint16_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8 ]; };struct in_addr { uint32_t s_addr; }; struct sockaddr { unsigned short int sa_family; char sa_data[14 ]; };
e.g.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 struct sockaddr_in servaddr{}; memset (&servaddr,0 ,sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl (INADDR_ANY); servaddr.sin_port = htons (atoi (argv[1 ])); if (bind (listenfd,(struct sockaddr *)&servaddr,sizeof (servaddr)) != 0 ) { perror ("bind" ); close (listenfd); return -1 ; }struct sockaddr_in srvaddr;bzero (&srvaddr,sizeof (srvaddr)); srvaddr.sin_family=AF_INET; srvaddr.sin_port=htons (PORT);if (inet_aton ("127.0.0.1" ,srvaddr.sin_addr.s_addr)==-1 ){ printf ("addr convert error\n" ); exit (1 ); }if (bind (sockfd,(struct sockaddr *)&srvaddr,sizeof (struct sockaddr))==-1 ){ printf ("bindt error\n" ); exit (1 ); }
将socket设置为监听模式: int listen(int s,int backlog)
-> 成功返回0, 失败返回-1
backlog
参数指定同时能处理的最大连接要求, 如若连接数目达到此上限则client端将收到错误
listen()
并没有真正开始接收连线, 只是设置socket为listen模式
listen()
通常在socket()
和bind()
之后调用, 接着调用accept()
e.g.
1 2 3 4 5 if (listen (listenfd,5 ) != 0 ) { perror ("listen" ); close (listenfd); return -1 ; }
接受客户端的连接: int accept(int s,struct sockaddr* addr,int* addrlen);
->失败返回-1
结构addr的定义参考同bind
1 2 3 4 5 6 int clientfd; int socklen=sizeof (struct sockaddr_in); struct sockaddr_in clientaddr{}; clientfd=accept (listenfd,(struct sockaddr *)&clientaddr,(socklen_t *)&socklen);printf ("客户端(%s)已连接。\n" ,inet_ntoa (clientaddr.sin_addr));
进行通信: int recv(int s,void *buf, int len, unsigned int flags);
int send(int s,const void * msg,int len,unsigned int falgs);
分别用于收发, 使用见下面的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 char buffer[1024 ];while (1 ) { int iret; memset (buffer,0 ,sizeof (buffer)); if ( (iret=recv (clientfd, buffer, sizeof (buffer), 0 )) <= 0 ) { printf ("iret=%d\n" ,iret); break ; } printf ("接收:%s\n" ,buffer); strcpy (buffer,"ok" ); if ( (iret=send (clientfd, buffer, strlen (buffer), 0 )) <= 0 ) { perror ("send" ); break ; } printf ("发送:%s\n" ,buffer); }if (listen (sockfd,BACKLOG)==-1 ){ printf ("listen error\n" ); exit (1 ); }for (;;){ sin_size=sizeof (struct sockaddr_in); if ((new_fd=accept (sockfd,(struct sockaddr *)&clientaddr,&sin_size))==-1 ){ printf ("accept errot\n" ); continue ; } nbytes=read (new_fd,buf,MAXDATASIZE); buf[nbytes]='\0' ; printf ("client:%s\n" ,buf); printf ("client:%d\n" ,nbytes); sprintf (buf,"wellcome!" ); write (new_fd,buf,strlen (buf)); close (new_fd); }
2 | Client端 创建socket 同server端:
1 2 3 4 5 6 7 int sockfd;if ((sockfd = socket (AF_INET, SOCK_STREAM, 0 )) == -1 ) { perror ("socket" ); return -1 ; }
向服务器发起连接请求 struct hostent *gethostbyname(const char *name);
:
把ip地址或域名转换为hostent 结构体表达的地址。
参数name,域名或者主机名,例如”127.0.0.1”、”blog.roccoshi.top”等。
返回值:如果成功,返回一个hostent结构指针,失败返回NULL。
1 2 3 4 5 6 7 8 9 10 11 struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; #ifdef __USE_MISC # define h_addr h_addr_list[0] #endif };
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
connect函数用于将参数sockfd 的socket 连至参数serv_addr 指定的服务端,参数addrlen为sockaddr的结构长度。
返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
connect函数只用于客户端。
如果服务端的地址错了,或端口错了,或服务端没有启动,connect一定会失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct hostent *h;if ((h = gethostbyname (argv[1 ])) == 0 ) { printf ("gethostbyname failed.\n" ); close (sockfd); return -1 ; }struct sockaddr_in servaddr;memset (&servaddr, 0 , sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons (atoi (argv[2 ])); memcpy (&servaddr.sin_addr, h->h_addr, h->h_length);if (connect (sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) != 0 ) { perror ("connect" ); close (sockfd); return -1 ; }
与服务器端通信 这部分和server端基本一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 char buffer[1024 ];for (int ii = 0 ; ii < 3 ; ii++) { int iret; memset (buffer, 0 , sizeof (buffer)); sprintf (buffer, "这是第%d个client,编号%03d。" , ii + 1 , ii + 1 ); if ((iret = send (sockfd, buffer, strlen (buffer), 0 )) <= 0 ) { perror ("send" ); break ; } printf ("发送:%s\n" , buffer); memset (buffer, 0 , sizeof (buffer)); if ((iret = recv (sockfd, buffer, sizeof (buffer), 0 )) <= 0 ) { printf ("iret=%d\n" , iret); break ; } printf ("接收:%s\n" , buffer); }
3 | 实验与附件 client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> int main (int argc, char *argv[]) { if (argc != 3 ) { printf ("Using:./client ip port\nExample:./client 127.0.0.1 5005\n\n" ); return -1 ; } int sockfd; if ((sockfd = socket (AF_INET, SOCK_STREAM, 0 )) == -1 ) { perror ("socket" ); return -1 ; } struct hostent *h; if ((h = gethostbyname (argv[1 ])) == 0 ) { printf ("gethostbyname failed.\n" ); close (sockfd); return -1 ; } struct sockaddr_in servaddr; memset (&servaddr, 0 , sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons (atoi (argv[2 ])); memcpy (&servaddr.sin_addr, h->h_addr, h->h_length); if (connect (sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) != 0 ) { perror ("connect" ); close (sockfd); return -1 ; } char buffer[1024 ]; while (true ) { printf ("please enter a string to server: " ); scanf ("%s\n" , buffer); if (send (sockfd, buffer, strlen (buffer), 0 ) == -1 ) { perror ("send" ); break ; } memset (buffer, 0 , sizeof (buffer)); if (recv (sockfd, buffer, sizeof (buffer), 0 ) == -1 ) { perror ("recv" ); break ; } printf ("recv: %s\n" , buffer); if (strcmp (buffer, "bye" ) == 0 ) break ; } close (sockfd); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <iostream> using namespace std;int main (int argc,char *argv[]) { if (argc!=2 ) { cout << "add the server port. \n E.g. ./server 4567" << endl; return -1 ; } int listenfd; if ( (listenfd = socket (AF_INET,SOCK_STREAM,0 )) == -1 ) { perror ("socket" ); return -1 ; } struct sockaddr_in servaddr{}; memset (&servaddr,0 ,sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl (INADDR_ANY); servaddr.sin_port = htons (atoi (argv[1 ])); if (bind (listenfd,(struct sockaddr *)&servaddr,sizeof (servaddr)) != 0 ) { perror ("bind" ); close (listenfd); return -1 ; } if (listen (listenfd,5 ) != 0 ) { perror ("listen" ); close (listenfd); return -1 ; } int clientfd; int socklen=sizeof (struct sockaddr_in); struct sockaddr_in clientaddr{}; clientfd=accept (listenfd,(struct sockaddr *)&clientaddr,(socklen_t *)&socklen); printf ("client(%s)is connected.\n" ,inet_ntoa (clientaddr.sin_addr)); char buffer[1024 ]; while (true ) { bool flag = false ; memset (buffer, 0 , sizeof (buffer)); if (recv (clientfd, buffer, sizeof (buffer), 0 ) == -1 ) { perror ("recv" ); break ; } printf ("recv: %s\n" , buffer); if (strcmp (buffer, "bye" ) != 0 ) { strcpy (buffer, "ok" ); } else { flag = true ; } if (send (clientfd, buffer, strlen (buffer), 0 ) == -1 ) { perror ("send" ); break ; } if (flag) break ; } close (listenfd); close (clientfd); }
reference:
bilibili: https://www.bilibili.com/video/BV11Z4y157RY?p=9&spm_id_from=pageDriver
C语言技术网: http://www.freecplus.net/44e059cca66042f0a1286eb188c51480.html
socket命令: https://wws.lanzous.com/i9OIpn8kmgj