具體生成動態庫的操作及使用該動態庫的操作請參見上篇博文。以下僅僅列出改進版本的代碼。
my_socket.h
#ifndef __MY_SOCKET_H__ #define __MY_SOCKET_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #define IN #define OUT #define IN_OUT #define MY_TCP 1 #define MY_UDP 2 typedef struct sockaddr* pSA ; typedef struct sockaddr_in SA ; #define MY_ASSERT(flag,msg) ( (flag) ? NULL : ( fprintf(stdout,msg), exit(EXIT_FAILURE) ) ) // NULL代表什麼也不做 void my_socket(OUT int *local_sfd, int protocal, char *local_ip, int local_port); void my_listen(int local_sfd, int backlog); void my_accept(OUT int *peer_sfd, int local_sfd, OUT pSA peer_addr, IN_OUT int *addr_len ); void my_connect(int local_sfd, char *peer_ip, int peer_port); void my_recv(OUT int *recv_len, int peer_sfd, IN_OUT void *base, int len); void my_send(OUT int *send_len, int peer_sfd, void *base, int len); void my_recvfrom(OUT int *recvfrom_len, int peer_sfd, IN_OUT void *base, int len, OUT char* peer_ip, OUT int *peer_port); void my_sendto(OUT int *sendto_len, int peer_sfd, OUT void *base, int len, char *peer_ip, int peer_port); void my_close(int sfd); #endif
my_socket.c
/*************************************************************************
> File Name: my_socket.c
> Author: KrisChou
> Mail:zhoujx0219@163.com
> Created Time: Mon 01 Sep 2014 06:54:48 PM CST
************************************************************************/
/* 本代碼用於在一台主機上模擬socket通信。因此IP地址對於server和client而言是一樣的。
* 為了簡化代碼,此處即使是客戶端,也提前分配好端口號。事實上,主動方的端口號可以由系統分配,不用提前綁定
* --> 無論server或者client,都會預先綁定本地socket */
/* 本代碼local_sfd代表本地socket描述符。
* 對於服務器而言,就是用於監聽的socket; 對於客戶端而言就是用於通信的socket
* peer_sfd,代表與對方通信的socket描述符。
* 對於服務器而言,由accept以傳出參數形式返回;對於客戶端而言,就是本地socket */
#include "my_socket.h"
void my_socket(OUT int *local_sfd, int protocal, char *local_ip, int local_port)
{
MY_ASSERT(protocal == MY_TCP || protocal == MY_UDP, "wrong arg! protocal is MY_TCP or MY_UDP! \n");
/* 創建本地socket */
if(protocal == MY_TCP)
{
MY_ASSERT(-1 != (*local_sfd = socket(AF_INET, SOCK_STREAM, 0)), "tcp_socket init falure!\n");
int reuse = 1;
setsockopt(*local_sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse));
}else if(protocal == MY_UDP)
{
MY_ASSERT(-1 != (*local_sfd = socket(AF_INET, SOCK_DGRAM, 0)), "udp_socket init failure!\n");
}
/* 將本地聯系方式bind到本地socket */
SA local_addr;
memset(&local_addr, 0, sizeof(SA));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(local_port);
local_addr.sin_addr.s_addr = inet_addr(local_ip);
MY_ASSERT( 0 == bind(*local_sfd, (pSA)&local_addr, sizeof(SA)), "bind failure!\n");
}
/*----------------------------- 以下針對TCP ----------------------------------------------------- */
/* server: listen + accept */
void my_listen(int local_sfd, int backlog)
{
MY_ASSERT( 0 == listen(local_sfd, backlog), "listen failure!\n");
}
void my_accept(OUT int *peer_sfd, int local_sfd, OUT pSA peer_addr, IN_OUT int *addr_len )
{
MY_ASSERT(-1 != (*peer_sfd = accept(local_sfd, peer_addr, addr_len)), "accept failure!\n");
}
/* client: connect */
void my_connect(int local_sfd, char *peer_ip, int peer_port )
{
int cnt = 0;
SA peer_addr;
memset(&peer_addr, 0, sizeof(SA));
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = htons(peer_port);
peer_addr.sin_addr.s_addr = inet_addr(peer_ip);
// 10次連不上就退出程序
while(-1 == connect(local_sfd, (pSA)&peer_addr, sizeof(SA)))
{
cnt++;
if(cnt == 10)
{
printf("connect failure!\n");
exit(EXIT_FAILURE);
}
sleep(1);
}
}
/* recv and send */
void my_recv(OUT int *recv_len, int peer_sfd, IN_OUT void *base, int len)
{
int recvn;
int recv_sum = 0;
while(recv_sum < len)
{
MY_ASSERT(-1 != (recvn = recv(peer_sfd, base + recv_sum, len - recv_sum, 0)), "recv error! \n");
recv_sum += recvn;
}
if(recv_len != NULL)
{
*recv_len = recv_sum;
}
}
void my_send(OUT int *send_len, int peer_sfd, void *base, int len)
{
int sendn;
int send_sum = 0;
while(send_sum < len)
{
MY_ASSERT(-1 != (sendn = send(peer_sfd, base + send_sum, len - send_sum, 0)), "send error! \n");
send_sum += sendn;
}
if(send_len != NULL)
{
*send_len = send_sum;
}
}
/*---------------------------- 以下針對UDP--------------------------------------------------------*/
void my_recvfrom(OUT int *recvfrom_len, int peer_sfd, IN_OUT void *base, int len, OUT char* peer_ip, OUT int *peer_port)
{
int recvn;
SA peer_addr;
int addr_len = sizeof(SA);
MY_ASSERT(-1 != (recvn = recvfrom(peer_sfd, base, len, 0, (pSA)&peer_addr, &addr_len)), "recvfrom failure!\n");
if(recvfrom_len != NULL)
{
*recvfrom_len = recvn;
}
if(peer_ip != NULL)
{
char *p = inet_ntoa(peer_addr.sin_addr);
MY_ASSERT(strlen(peer_ip) >= strlen(p) + 1, "buf for ip is too short! \n");
strcpy(peer_ip, p);
}
if(peer_port != NULL)
{
*peer_port = ntohs(peer_addr.sin_port);
}
}
void my_sendto(OUT int *sendto_len, int peer_sfd, OUT void *base, int len, char *peer_ip, int peer_port)
{
int sendn;
SA peer_addr;
memset(&peer_addr, 0, sizeof(SA));
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = htons(peer_port);
peer_addr.sin_addr.s_addr = inet_addr(peer_ip);
MY_ASSERT(-1 != (sendn = sendto(peer_sfd, base, len, 0, (pSA)&peer_addr, sizeof(SA))), "sendto failure!\n");
if(sendto_len != NULL)
{
*sendto_len = sendn;
}
}
/* close */
void my_close(int sfd)
{
MY_ASSERT(0 == close(sfd), "close failure!\n");
}
server.c
#include "my_socket.h"
#define IP "192.168.153.128"
#define PORT 8888
int main(int argc, char *argv[])
{
int fd_server, fd_client;
int val; //用4個字節的地址空間來傳數據
int len;
my_socket(&fd_server, MY_TCP, IP, PORT);
my_listen(fd_server,5);
my_accept(&fd_client, fd_server, NULL, NULL);
printf("accept success!\n");
while(1)
{
//my_accept(&fd_client, fd_server, NULL, NULL);
//printf("accept success!\n");
my_recv(&len, fd_client, (void*)&val, sizeof(val));
printf("recv data: %d\n", val);
my_send(&len, fd_client, (void*)&val, sizeof(val));
printf("%d has sent!\n\n", val);
}
my_close(fd_client);
my_close(fd_server);
return 0;
}
client.c
#include "my_socket.h"
#define IP "192.168.153.128"
#define MY_PORT 6666
#define SERVER_PORT 8888
int main(int argc, char *argv[])
{
/* socket */
int fd_client;
my_socket(&fd_client, MY_TCP, IP, MY_PORT);
/* connect */
my_connect(fd_client, IP, SERVER_PORT);
printf("connect success!\n");
/* 發送一個數據,並從服務器端返回這個數據 */
int val_in,val_out,len;
while(scanf("%d", &val_in) == 1)
{
my_send(NULL,fd_client,(void*)&val_in,sizeof(int));
my_recv(NULL,fd_client,(void*)&val_out,sizeof(int));
printf("recv fron server: %d\n", val_out);
}
my_close(fd_client);
return 0;
}
tcp能確保你的包正確的發送到了指定地址,而udp只負責發送,不管是否送達及送達的包是否正確。
是的,從TCP/IP四層模型可以看出。TCP和UDP是在第三層傳輸層。而IP在第二層網際層。數據從上層封裝到下層。所以TCP和UDP被封裝在IP包裡。另外:TCP和UDP是不同的傳輸方式。不能同時出現!