前言
socket编程现在几乎成了网络编程的代名词,关于这方面的书籍与资料更是不可胜数。比较好的可以推荐一下《Unix 网络编程 卷1:套接字联网API》。
1. TCP
TCP提供客户与服务器之间的连接(connection),TCP客户先与某个给定服务器之间建立一个连接,再跨该连接与那个服务器交换数据,然后终止这个连接。
1.1 TCP server
编程步骤:
socket→初始化struct sockaddr_in→bind→listen→[accept→send→recv]
tcp_server.c
- /*
- * \brief
- * tcp server
- */
- #include <stdio.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <string.h>
- #include <stdlib.h>
- #define SERVPORT 8080
- #define BACKLOG 10 // max numbef of client connection
- #define MAXDATASIZE 100
- int main(char argc, char *argv[])
- {
- int sockfd, client_fd, addr_size, recvbytes;
- char rcv_buf[MAXDATASIZE], snd_buf[MAXDATASIZE];
- char* val;
- struct sockaddr_in server_addr;
- struct sockaddr_in client_addr;
- char IPdotdec[20];
- /* create a new socket and regiter it to os .
- * SOCK_STREAM means that supply tcp service,
- * and must connect() before data transfort.
- */
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- {
- perror("socket:");
- exit(1);
- }
- /* setting server's socket */
- server_addr.sin_family = AF_INET; // IPv4 network protocol
- server_addr.sin_port = htons(SERVPORT);
- server_addr.sin_addr.s_addr = INADDR_ANY; // auto IP detect
- memset(&(server_addr.sin_zero),0, 8);
- if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))== -1)
- {
- perror("bind:");
- exit(1);
- }
- /* watting for connection , and server permit to recive the requestion from sockfd */
- if (listen(sockfd, BACKLOG) == -1) // BACKLOG assign thd max number of connection
- {
- perror("listen:");
- exit(1);
- }
- while(1)
- {
- addr_size = sizeof(struct sockaddr_in);
- /* accept the sockfd's connection,
- * return an new socket and assign far host to client_addr
- */
- if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_size)) == -1)
- {
- /* Nonblocking mode */
- perror("accept:");
- continue;
- }
- /* network-digital to ip address */
- inet_ntop(AF_INET, (void*)&client_addr, IPdotdec, 16);
- printf("connetion from:%d : %s\n",client_addr.sin_addr, IPdotdec);
- if (!fork())
- {
- /* child process handle with the client connection */
- /* recive the client's data by client_fd */
- if ((recvbytes = recv(client_fd, rcv_buf, MAXDATASIZE, 0)) == -1)
- {
- perror("recv:");
- exit(1);
- }
- rcv_buf[recvbytes]='\0';
- printf("recv:%s\n", rcv_buf);
- *snd_buf='\0';
- strcat(snd_buf, "welcome");
- /* send the message to far-hosts by client_fd */
- if (send(client_fd, snd_buf, strlen(snd_buf), 0) == -1)
- {
- perror("send:");
- exit(1);
- }
- printf("send:%s\n", snd_buf);
- close(client_fd);
- exit(1);
- }
- close(client_fd);
- }
- return 0;
- }
编译:
- gcc -g -o tcp_server tcp_server.c
1.2 TCP client
编程步骤:
socket→初始化struct sockaddr_in→connect→[recv→send]
tcp_client.c
- /*
- * \brief
- * tcp client
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <string.h>
- #define SERVPORT 8080
- #define MAXDATASIZE 100
- int main(int argc, char *argv[])
- {
- int sockfd, recvbytes;
- char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */
- char snd_buf[MAXDATASIZE];
- struct hostent *host; /* struct hostent
- * {
- * char *h_name; // general hostname
- * char **h_aliases; // hostname's alias
- * int h_addrtype; // AF_INET
- * int h_length;
- * char **h_addr_list;
- * };
- */
- struct sockaddr_in server_addr;
- if (argc < 3)
- {
- printf("Usage:%s [ip address] [any string]\n", argv[0]);
- return 1;
- }
- *snd_buf = '\0';
- strcat(snd_buf, argv[2]);
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- {
- perror("socket:");
- exit(1);
- }
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(SERVPORT);
- inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
- memset(&(server_addr.sin_zero), 0, 8);
- /* create the connection by socket
- * means that connect "sockfd" to "server_addr"
- */
- if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
- {
- perror("connect");
- exit(1);
- }
- /**/
- if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1)
- {
- perror("send:");
- exit(1);
- }
- printf("send:%s\n", snd_buf);
- if ((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, 0)) == -1)
- {
- perror("recv:");
- exit(1);
- }
- rcv_buf[recvbytes] = '\0';
- printf("recv:%s\n", rcv_buf);
- close(sockfd);
- return 0;
- }
编译:
- gcc -o -g tcp_client tcp_client.c
2. UDP
UDP提供无连接的(connectionless)服务,比如一个UDP客户可以创建一个套接字并发送一个数据报给一个给定的服务器,然后立即用同一个套接字发送另一个数据报给另一个服务器。同样地,一个UDP服务器可以用同一个UDP套接字从若干不同的客户接收数据报,每个客户一个数据报。
2.1 UDP server
编程步骤:
socket→初始化struct sockaddr_in→bind→[recvfrom→sendto]
udp_server.c
- #include <stdio.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <string.h>
- #include <stdlib.h>
- #define SERVPORT 8080
- #define MAXDATASIZE 100
- int main()
- {
- int sockfd, addr_size, recvbytes;
- char rcv_buf[MAXDATASIZE], snd_buf[MAXDATASIZE];
- char* val;
- struct sockaddr_in server_addr;
- if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- {
- perror("socket:");
- exit(1);
- }
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(SERVPORT);
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- memset(&(server_addr.sin_zero), 0, 8);
- if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1)
- {
- perror("bind:");
- exit(1);
- }
- while(1)
- {
- addr_size = sizeof(struct sockaddr);
- /* receive the data from "server_addr" by "sockfd" and store into "rcv_buf" */
- if ((recvbytes = recvfrom(sockfd, rcv_buf, MAXDATASIZE, 0,
- (struct sockaddr*)&server_addr, &addr_size)) == -1)
- {
- perror("recv:");
- exit(1);
- }
- rcv_buf[recvbytes] = '\0';
- printf("recv:%s : %s\n", inet_ntop(server_addr.sin_addr), rcv_buf);
- *snd_buf = '\0';
- strcat(snd_buf, "welcome");
- /* send the data "snd_buf" to "server_addr" by "sockfd" */
- if (sendto(sockfd, snd_buf, strlen(snd_buf), 0,
- (struct sockaddr*)&server_addr, addr_size) == -1)
- {
- perror("send:");
- exit(1);
- }
- printf("send:%s\n", snd_buf);
- //exit(1);
- }
- close(sockfd);
- return 0;
- }
编译:
- gcc -g -o udp_server udp_server.c
2.2 UDP client
编程步骤:
socket→初始化struct sockaddr_in→[sendto→recvfrom]
udp_client.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <string.h>
- #define SERVPORT 8080
- #define MAXDATASIZE 100
- int main(int argc, char *argv[])
- {
- int server_sockfd, recvbytes,addr_size;
- char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello*/
- char snd_buf[MAXDATASIZE];
- struct hostent *host;
- struct sockaddr_in server_addr;
- if (argc < 3)
- {
- printf("Usage:%s [ip address] [any string]\n",argv[0]);
- return 1;
- }
- *snd_buf = '\0';
- strcat(snd_buf,argv[2]);
- if ((server_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- {
- perror("socket:");
- exit(1);
- }
- server_addr.sin_family=AF_INET;
- server_addr.sin_port=htons(SERVPORT);
- inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
- memset(&(server_addr.sin_zero),0,8);
- addr_size=sizeof(struct sockaddr);
- /* send the data "snd_buf" to "server_addr" by server_sockfd */
- if (sendto(server_sockfd,snd_buf,sizeof(snd_buf),0,
- (struct sockaddr*)&server_addr,addr_size) == -1)
- {
- perror("send:");
- exit(1);
- }
- printf("send:%s\n",snd_buf);
- /* receive the data from "server_addr" by "server_sockfd" and store into rcv_buf */
- if ((recvbytes=recvfrom(server_sockfd,rcv_buf,MAXDATASIZE,0,
- (struct sockaddr*)&server_addr,&addr_size)) ==-1)
- {
- perror("recv:");
- exit(1);
- }
- rcv_buf[recvbytes] = '\0';
- printf("recv:%s\n",rcv_buf);
- close(server_sockfd);
- return 0;
- }
编译:
- gcc -g -o udp_client udp_client.c
3. UDP multicast
有时候,本机不止有一张网卡,所以接收UDP组播的数据先要将本机指定的网卡地址加入组播组,才能接收到组播数据。
编程步骤:
socket-->setsockopt-->bind->setsockopt(本机网卡加入组播组)-->[recvfrom --> sendto]
udp_multicast_recv.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #define UDPMCASTPORT 1234
- #define MAXDATASIZE 1024
- int main(char argc, char *argv[])
- {
- int udp_sockfd, addr_size, recvbytes;
- char rcv_buf[MAXDATASIZE];
- struct sockaddr_in servaddr, clitaddr;
- struct ip_mreq mreq;
- char* pc_udpmcaddr = "225.1.1.31";
- char* pc_udpmcport = "1234";
- char* pc_localaddr = "192.168.1.73";
- int i_reuse_socket;
- if (argc != 3)
- {
- fprintf(stdout, "SYNTAX: ./udp_mcast_recv <udp_multicast_address> <local_address>\n");
- exit(1);
- }
- else
- {
- pc_udpmcaddr = argv[1];
- pc_localaddr = argv[2];
- }
- memset(&servaddr, 0, sizeof(struct sockaddr_in));
- memset(&clitaddr, 0, sizeof(struct sockaddr_in));
- memset(&mreq, 0, sizeof(struct ip_mreq));
- /* create socket */
- if ((udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- {
- perror("socket:");
- exit(1);
- }
- i_reuse_socket = 1;
- if (setsockopt(udp_sockfd, SOL_SOCKET, SO_REUSEADDR, &i_reuse_socket, sizeof(int))== -1)
- {
- perror("setsockopt:");
- exit(1);
- }
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(UDPMCASTPORT);
- if (!inet_aton(pc_udpmcaddr, &servaddr.sin_addr))
- {
- perror("inet_aton:");
- exit(1);
- }
- if (bind(udp_sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr))==-1)
- {
- perror("bind:");
- exit(1);
- }
- if (!inet_aton(pc_udpmcaddr, &mreq.imr_multiaddr))
- {
- perror("inet_aton:");
- exit(1);
- }
- if (!inet_aton(pc_localaddr, &mreq.imr_interface))
- {
- perror("inet_aton:");
- exit(1);
- }
- if (setsockopt(udp_sockfd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))==-1)
- {
- perror("setsockopt:");
- exit(1);
- }
- addr_size = sizeof(struct sockaddr);
- while(1)
- {
- if ((recvbytes = recvfrom(udp_sockfd, rcv_buf, MAXDATASIZE, 0,
- (struct sockaddr*)&clitaddr, &addr_size)) == -1)
- {
- perror("recvfrom:");
- exit(1);
- }
- rcv_buf[recvbytes] = '\0';
- fprintf(stdout, "recvfrom:%s:%s", inet_ntoa(clitaddr.sin_addr), rcv_buf);
- }
- }
编译:
- gcc -g -o udp_multicast_recv udp_multicast_recv.c