1、实现只能接受一个连接的tcp服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define MAXLINE 4096
int main(int argc, char** argv) {
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
if (listen(listenfd, 10) == -1) {
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
printf("=============waiting for client's request=============\n");
while (1)
{
if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
close(listenfd);
return 0;
}
用telnet连接你写的服务端,显示,it’s work。
2、通过fork实现能接受多个tcp连接的服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define MAXLINE 4096
#define MAX_CLIENT 1000
int main(int argc, char** argv) {
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
pid_t pid;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
if (listen(listenfd, MAX_CLIENT) == -1) {
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
printf("=============waiting for client's request=============\n");
while (1)
{
if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
exit(-1);
}
pid = fork();
printf("listenfd: %d", listenfd);
if (pid == 0) {
close(listenfd);
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
exit(0);
} else if (pid > 0) {
close(connfd);
}
}
close(listenfd);
return 0;
}
3、使用select实现tcp服务端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <assert.h>
#include <netdb.h>
#define BUF_SIZE 1024
void print_sockaddr(struct sockaddr_in addr)
{
// 保存点分十进制的地址
char ip_address[INET_ADDRSTRLEN];
int port;
inet_ntop(AF_INET, &addr.sin_addr, ip_address, sizeof(ip_address));
port = ntohs(addr.sin_port);
printf("(%s:%d)\n", ip_address, port);
}
int main(int argc, char *argv[])
{
int sfd = -1;
struct addrinfo hints;
struct addrinfo *result;
struct addrinfo *rp;
struct sockaddr_in client_addr;
struct sockaddr_in peer_addr;
fd_set read_fds;
fd_set work_fds;
struct timeval tout;
int peer_addrlen;
int client_addrlen;
int optval;
int max_sockfd;
char recv_buf[BUF_SIZE];
int port = 0;
if (argc != 2) {
fprintf(stderr, "usage: %s port\n", argv[0]);
exit(1);
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* 允许IPv4 或者 IPv6 */
hints.ai_socktype = SOCK_STREAM; /* TCP */
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
int s = getaddrinfo(NULL, argv[1], &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if ((setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
&optval, sizeof (optval))) != 0)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != 0)
continue;
if (listen (sfd, 5) != 0)
continue;
/* 成功 */
break;
}
if (rp == NULL) {
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
FD_ZERO(&read_fds);
FD_SET(sfd, &read_fds);
FD_SET(STDIN_FILENO, &read_fds);
max_sockfd = sfd;
while (1) {
tout.tv_sec = 2;
tout.tv_usec = 0;
work_fds = read_fds;
int ret = select (max_sockfd + 1, &work_fds, NULL, NULL, &tout);
if (ret == 0) {
continue;
}
if (ret == -1) {
continue;
}
for (int i = 0; i < max_sockfd + 1; i++) {
if (!FD_ISSET(i, &work_fds)) {
continue;
}
int fd = i;
if (fd == sfd) {
client_addrlen = sizeof(client_addr);
int cfd = accept(sfd, (struct sockaddr *)&client_addr,
(socklen_t *)&client_addrlen);
printf("accept fd:%d ", cfd);
print_sockaddr(client_addr);
if (cfd < 0) {
printf("accept cfd < 0!");
continue;
}
FD_SET(cfd, &read_fds);
if (cfd > max_sockfd) {
max_sockfd = cfd;
}
} else {
ssize_t num_read = recv(fd, recv_buf, sizeof(recv_buf), 0);
if (num_read <= 0) {
printf ("client has left fd:%d\n", fd);
close(fd);
FD_CLR(fd, &read_fds);
continue;
}
recv_buf[num_read] = '\0';
printf("receive %zd bytes: \"%s\" from fd:%d", num_read, recv_buf, fd);
peer_addrlen = sizeof(peer_addr);
getpeername(fd, (struct sockaddr *)&peer_addr, (socklen_t *)&peer_addrlen);
print_sockaddr(peer_addr);
send(fd, recv_buf, (size_t)num_read, 0);
}
}
}
return EXIT_SUCCESS;
}
4、实现tcp连接的客户端
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 6666
#define MAXDATASIZE 4096
int main(int argc, char *argv[])
{
int sockfd, num;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server;
if (argc != 2) {
printf("Usage: %s <IP Address>\n", argv[0]);
exit(1);
}
if ((he = gethostbyname(argv[1])) == NULL) {
printf("gethostbyname() error\n");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("socket() error\n");
exit(1);
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr *)he -> h_addr);
if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1) {
printf("connect() error\n");
exit(1);
}
char str[] = "horst\n";
if ((num = send(sockfd, str, sizeof(str), 0)) == -1) {
printf("send() error\n");
exit(1);
}
if ((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
printf("recv() error\n");
exit(1);
}
buf[num - 1] = '\0';
printf("server message: %s\n", buf);
close(sockfd);
return 0;
}
不再使用telnet测试第三步的代码,使用上方代码作为tcp client测试tcp连接,测试成功
5、实现udp服务端
udp相比tcp就更简单了
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define SERV_PORT 8000
int main()
{
/* sock_fd --- socket文件描述符 创建udp套接字*/
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
{
perror("socket");
exit(1);
}
/* 将套接字和IP、端口绑定 */
struct sockaddr_in addr_serv;
int len;
memset(&addr_serv, 0, sizeof(struct sockaddr_in)); //每个字节都用0填充
addr_serv.sin_family = AF_INET;//使用IPV4地址
addr_serv.sin_port = htons(SERV_PORT);//端口
/* INADDR_ANY表示不管是哪个网卡接收到数据,只要目的端口是SERV_PORT,就会被该应用程序接收到 */
addr_serv.sin_addr.s_addr = htonl(INADDR_ANY); //自动获取IP地址
len = sizeof(addr_serv);
/* 绑定socket */
if(bind(sock_fd, (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0)
{
perror("bind error:");
exit(1);
}
int recv_num;
int send_num;
char send_buf[20] = "i am server!";
char recv_buf[20];
struct sockaddr_in addr_client;
while(1)
{
printf("server wait:\n");
recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, (socklen_t *)&len);
if(recv_num < 0)
{
perror("recvfrom error:");
exit(1);
}
recv_buf[recv_num] = '\0';
printf("server receive %d bytes: %s\n", recv_num, recv_buf);
send_num = sendto(sock_fd, send_buf, recv_num, 0, (struct sockaddr *)&addr_client, len);
if(send_num < 0)
{
perror("sendto error:");
exit(1);
}
}
close(sock_fd);
return 0;
}
到这步仍无法测试代码是否运行成功,telnet默认是tcp协议,不支持udp,需要实现udp client才能测试
6、实现udp client
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define DEST_PORT 8000
#define DSET_IP_ADDRESS "127.0.0.1"
int main()
{
/* socket文件描述符 */
int sock_fd;
/* 建立udp socket */
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
{
perror("socket");
exit(1);
}
/* 设置address */
struct sockaddr_in addr_serv;
int len;
memset(&addr_serv, 0, sizeof(addr_serv));
addr_serv.sin_family = AF_INET;
addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);
addr_serv.sin_port = htons(DEST_PORT);
len = sizeof(addr_serv);
int send_num;
int recv_num;
char send_buf[20] = "hey, who are you?";
char recv_buf[20];
printf("client send: %s\n", send_buf);
send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);
if(send_num < 0)
{
perror("sendto error:");
exit(1);
}
recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);
if(recv_num < 0)
{
perror("recvfrom error:");
exit(1);
}
recv_buf[recv_num] = '\0';
printf("client receive %d bytes: %s\n", recv_num, recv_buf);
close(sock_fd);
return 0;
}
测试成功,udp server和client都正确实现了。上述代码都没有使用框架,框架底层基本都是这样实现的。