三次握手及四次挥手
Socket 起源于 Unix,而Unix基本哲学之一就是一切皆文件,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符
通信模型:
Socket三次握手及四次挥手过程:
tcp标志位说明:
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
三次握手过程:
(1)第一次握手:客户端向服务器发送SYN包,假设序列号为x,进入SYN_SEND状态。
(2)第二次握手:服务器收到SYN包,进行确认,发送ACK报文,序列号为x+1,同时自己也发送一个SYN包(假设序列号为y),此时服务器进入SYN_RECV状态。
(3)第三次握手:客户端收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(确认号为y+1),客户端和服务器进入ESTABLISHED状态,完成三次握手。
四次挥手过程:
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。
(3)然后服务器B也会发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,确认序号为收到序号加1。
主动关闭连接的一方收到FIN后会进入TIME_WAIT状态。这样设计主要有两个原因:
(1)可靠地实现TCP连接的终止。四次挥手过程中,如果客户端最终发送的ACK丢失,服务器会重发FIN,通过保持TIME_WAIT状态可以允许它重发ACK,保证TCP连接正常地终止。
(2)让旧连接的重复分组在网络中消失。因为如果复用旧连接的端口马上开启一个新的连接,原来旧连接中的数据包可能会被这个新连接收到。处于TIME_WAIT状态的连接就可以避免这种情况。它会持续2个最大分解生命期(MSL),也就是一个IP数据包能在网络中生存的最长时间,保证新连接建立的时候,旧连接的数据包已经在网络中消失了。
Socket C示例
接口说明:
/************************************************************************************************************************
1、int socket(int family,int type,int protocol)
family:
指定使用的协议簇:AF_INET(IPv4) AF_INET6(IPv6) AF_LOCAL(UNIX协议) AF_ROUTE(路由套接字) AF_KEY(秘钥套接字)。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:
指定使用的套接字的类型:SOCK_STREAM(字节流套接字) SOCK_DGRAM
protocol:
如果套接字类型不是原始套接字,那么这个参数就为0
2、int bind(int sockfd, struct sockaddr *myaddr, int addrlen)
sockfd:
socket函数返回的套接字描述符
myaddr:
是指向本地IP地址的结构体指针
myaddrlen:
结构长度
struct sockaddr{
unsigned short sa_family; //通信协议类型族AF_xx
char sa_data[14]; //14字节协议地址,包含该socket的IP地址和端口号
};
struct sockaddr_in{
short int sin_family; //通信协议类型族
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //IP地址
unsigned char si_zero[8]; //填充0以保持与sockaddr结构的长度相同
3、int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen)
sockfd:
socket函数返回套接字描述符
serv_addr:
服务器IP地址结构指针
addrlen:
结构体指针的长度
4、int listen(int sockfd, int backlog)
sockfd:
socket函数绑定bind后套接字描述符
backlog:
设置可连接客户端的最大连接个数,当有多个客户端向服务器请求时,收到此值的影响。默认值20
5、int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen)
sockfd:
socket函数经过listen后套接字描述符
cliaddr:
客户端套接字接口地址结构
addrlen:
客户端地址结构长度
6、int send(int sockfd, const void *msg,int len,int flags)
7、int recv(int sockfd, void *buf,int len,unsigned int flags)
sockfd:
socket函数的套接字描述符
msg:
发送数据的指针
buf:
存放接收数据的缓冲区
len:
数据的长度,把flags设置为0
*************************************************************************************************************************/
sockaddr_in
socket分为服务器端和客户端,服务器端监听端口发来的请求,收到后向客户端发送一个Hello World,客户机负责发送消息并打印收到的Hello World.
服务器步骤:建立socket,绑定socket和地址信息,开启监听,收到请求后发送数据。
客户端步骤:建立socket,连接服务器端,接收并打印服务器给的数据
服务器:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#define PORT 1500//端口号
#define BACKLOG 5/*最大监听数*/
int main(){
int sockfd,new_fd;/*socket句柄和建立连接后的句柄*/
struct sockaddr_in my_addr;/*本方地址信息结构体,下面有具体的属性赋值*/
struct sockaddr_in their_addr;/*对方地址信息*/
int sin_size;
sockfd=socket(AF_INET,SOCK_STREAM,0);//建立socket
if(sockfd==-1){
printf("socket failed:%d",errno);
return -1;
}
my_addr.sin_family=AF_INET;/*该属性表示接收本机或其他机器传输*/
my_addr.sin_port=htons(PORT);/*端口号*/
my_addr.sin_addr.s_addr=htonl(INADDR_ANY);/*IP,括号内容表示本机IP*/
bzero(&(my_addr.sin_zero),8);/*将其他属性置0*/
if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0){//绑定地址结构体和socket
printf("bind error");
close(sockfd);
return -1;
}
listen(sockfd,BACKLOG);//开启监听 ,第二个参数是最大监听数
while(1){
sin_size=sizeof(struct sockaddr_in);
new_fd=accept(sockfd,(struct sockaddr*)&their_addr,&sin_size);//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
if(new_fd==-1){
printf("receive failed");
} else{
printf("receive success");
send(new_fd,"Hello World!",12,0);//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
}
}
return 0;
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#define DEST_PORT 1500//目标地址端口号
#define DEST_IP "127.0.0.1"/*目标地址IP,这里设为本机*/
#define MAX_DATA 100//接收到的数据最大程度
int main(){
int sockfd,new_fd;/*cocket句柄和接受到连接后的句柄 */
struct sockaddr_in dest_addr;/*目标地址信息*/
char buf[MAX_DATA];//储存接收数据
sockfd=socket(AF_INET,SOCK_STREAM,0);/*建立socket*/
if(sockfd==-1){
printf("socket failed:%d",errno);
}
//参数意义见上面服务器端
dest_addr.sin_family=AF_INET;
dest_addr.sin_port=htons(DEST_PORT);
dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);
bzero(&(dest_addr.sin_zero),8);
if(connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))==-1){//连接方法,传入句柄,目标地址和大小
printf("connect failed:%d",errno);//失败时可以打印errno
close(sockfd);
return -1;
} else{
printf("connect success");
recv(sockfd,buf,MAX_DATA,0);//将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。
printf("Received:%s",buf);
}
close(sockfd);//关闭socket
return 0;
}
sockaddr_un
进程间通信的另一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。socket接口中的协议族参数决定了使用的地址类型,AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合,而AF_UNIX决定要用一个绝对路径名作为地址。
服务端:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/un.h>
#define BACKLOG 5/*最大监听数*/
#define PATH "un.sock"
int main()
{
int fd,new_fd;/*socket句柄和建立连接后的句柄*/
struct sockaddr_un my_addr;/*本方地址信息结构体,下面有具体的属性赋值*/
struct sockaddr_un their_addr;/*对方地址信息*/
int size;
fd=socket(AF_UNIX,SOCK_STREAM,0);//建立socket
if (fd == -1) {
printf("socket failed:%d\n",errno);
return -1;
}
unlink(PATH);
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sun_family = AF_UNIX;
strcpy(my_addr.sun_path, PATH);
if (bind(fd,(struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0){//绑定地址结构体和socket
printf("bind error: %d\n", errno);
goto END;
}
listen(fd,BACKLOG);//开启监听 ,第二个参数是最大监听数
while (1) {
printf("wait accept...\n");
size=sizeof(struct sockaddr_un);
new_fd=accept(fd,(struct sockaddr*)&their_addr,&size);//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
if(new_fd==-1){
printf("receive failed\n");
} else{
printf("receive success. fd=%d, new_fd=%d\n", fd, new_fd);
send(new_fd,"Hello World!\n",13,0);//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
}
}
END:
close(fd);
return 0;
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/un.h>
#define PATH "un.sock"
#define MAX_DATA 100//接收到的数据最大程度
int main()
{
int fd;/*socket句柄和建立连接后的句柄*/
struct sockaddr_un s_addr;/*对方地址信息*/
char buf[MAX_DATA];//储存接收数据
int buf_len;
fd=socket(AF_UNIX,SOCK_STREAM,0);//建立socket
if (fd == -1) {
printf("socket failed:%d\n",errno);
return -1;
}
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sun_family = AF_UNIX;
strcpy(s_addr.sun_path, PATH);
if (connect(fd, (struct sockaddr*)&s_addr, sizeof(struct sockaddr)) < 0){//绑定地址结构体和socket
printf("connect error: %d\n", errno);
goto END;
} else {
printf("connect success, fd=%d\n", fd);
recv(fd,buf,MAX_DATA,0);//将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。
printf("Received:%s",buf);
}
END:
close(fd);
return 0;
}
socket分为阻塞和非阻塞两种。阻塞式的socket的recv服从这样的规则:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,阻塞直到缓冲区中有数据。非阻塞式的socket的recv服从的规则则是:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,产生EAGAIN的错误并返回(在Python中会抛出一个异常)。两种情况都不会返回空字符串,返回空数据的结果是对方关闭了连接之后才会出现的。下面的示例中会添加set_nonblock(fcntl函数)实现非阻塞socket。
select、poll和epoll
如何解决服务器多并发问题?
1、每个进程处理一个客户端
2、每个线程处理一个客户端
3、io复用模型
select、poll、epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。io复用模型只需要一个进程就够了,之所以能够同时处理多个客户端的请求,原因是可以查询哪个客户端准备好了,对于准备好的客户端(例如客户端已经发了信息过来,本服务器用read读取数据的时候不会阻塞;另外,客户端已经关闭了连接,那么本服务read的时候,返回0,也不会阻塞),则和它进行通信,而未准备好的,就暂时先不理会。
1,select 实现
select函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
//nfds: 最大的那个fd整数加1。
//fd_set: fd的集合。三个集合,分别是读、写、异常的集合,当置为空时表示用不到。可以将关心的fd放到对应的集合里去,然后交给内核去轮询。
//timeout: 代表超时时间,是struct timeval类型的结构体,参数有:long tv_sec(秒) 和long tv_usec(微秒),为空表示永不超时。
fd集合的操作:
void FD_CLR(int fd,fd_set *set);//将集合里对应的fd清掉
void FD_SET(int fd,fd_set *set);//将fd加到集合里
int FD_IS SET(int fd,fd_set *set);//是否准备好了,好了就返回非零值
void FD_ZERO(fd_set*set);//集合全部清空
select的几大缺点:
(1)每次调用select都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
2, poll 实现
poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多。
3、epoll 实现
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生:
-
调用epoll_create建立一个epoll对象(在epoll文件系统中给这个句柄分配资源);
-
调用epoll_ctl向epoll对象中添加这所有连接的套接字;
-
调用epoll_wait收集发生事件的连接。
epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?
对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果)。
对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max 查看,一般来说这个数目和系统内存关系很大。
总结:epoll采用事件触发的方式,当某个fd准备好后会触发事件,这样减少了内核的轮询。同时,epoll返回的是那些准备好的fd,避免程序员进行全部的轮询。另外,select的fd数上限一般是1024,但是epoll没有上限。
epoll函数原型:
1、int epoll_create(int size);
创建一个epoll接口,返回一个epoll描述符,后面要用到。
2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
用来从epoll接口中添加fd或删除、修改fd。
(1)epfd是接口描述符,
(2)op是添加、修改、删除三者,fd是要操作的对象,event是监视的事件。结构如下:
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
3、int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
(1)events是输出型的参数,返回的是准备好的描述符,
(2)maxevents是事件数量的上限
(3)timeout是超时,以毫秒为单位,-1代表用不超时,0代表不等待立即返回。
epoll的水平触发和边沿触发?
水平触发:epoll的事件默认情况下是水平触发。例如,只要客户端的数据仍然未读完,那么事件就会一直发生。告诉服务器,请将数据读出来。
边沿触发:加上了events那里加了EPOLLET这个选项后,变成边沿触发。也就是数据可读,则只触发一次事件,服务器必须一直读,直到把数据读完。
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/un.h>
#define MAX_FD_NUM 3
#define PATH "un.sock"
void setnonblock(int fd) {
int flag = fcntl(fd, F_GETFL, 0);
if (flag == -1) {
printf("get fcntl flag %s\n", strerror(errno));
return;
}
int ret = fcntl(fd, F_SETFL, flag | O_NONBLOCK);
if (ret == -1) {
printf("set fcntl non-blocking %s\n", strerror(errno));
return;
}
}
int socket_create() {
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) {
printf("socket create %s\n", strerror(errno));
return -1;
}
setnonblock(fd);
struct sockaddr_un addr;
unlink(PATH);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, PATH);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
printf("socket bind: %s\n", strerror(errno));
return -1;
}
if (listen(fd, 20) == -1) {
printf("socket listen %s\n", strerror(errno));
return -1;
}
return fd;
}
void socket_accept(int fd) {
struct epoll_event event, events[MAX_FD_NUM];
int client_fd;
int epfd = epoll_create(MAX_FD_NUM);
if (epfd == -1) {
printf("epoll create %s\n", strerror(errno));
return;
}
memset(&event, 0, sizeof(event));
event.data.fd = fd;
event.events = EPOLLIN | EPOLLERR | EPOLLHUP;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {
printf("epoll ctl %s\n", strerror(errno));
return;
}
while (1) {
int num = epoll_wait(epfd, events, MAX_FD_NUM, -1);
if (num == -1) {
printf("epoll wait %s\n", strerror(errno));
break;
} else {
int i = 0;
for (; i<num; ++i) {
if (events[i].data.fd == fd) {
struct sockaddr_un client_addr;
memset(&client_addr, 0, sizeof(client_addr));
int len = sizeof(client_addr);
client_fd = accept(fd, (struct sockaddr *)&client_addr, &len);
if (client_fd == -1) {
printf("socket accept %s\n", strerror(errno));
return;
} else {
printf("socket accept success. fd=%d, client_fd=%d\n", fd, client_fd);
}
setnonblock(client_fd);
event.data.fd = client_fd;
event.events = EPOLLIN | EPOLLERR | EPOLLHUP;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
printf("epoll ctl %s\n", strerror(errno));
return;
}
continue;
} else if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) {
printf("epoll err\n");
close(events[i].data.fd);
continue;
} else {
char buf[64];
memset(buf, 0, sizeof(buf));
recv(events[i].data.fd, buf, sizeof(buf), 0);
printf("recv msg: %s", buf);
close(events[i].data.fd);
continue;
}
}
}
}
}
int main(int argc, char *argv[]) {
int fd = socket_create();
if (fd == -1) {
printf("socket create fd failed\n");
return;
}
socket_accept(fd);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define PATH "un.sock"
int socket_connect() {
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) {
printf("socket create %s\n", strerror(errno));
return -1;
}
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, PATH);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
printf("socket connect %s\n", strerror(errno));
return -1;
}
return fd;
}
void socket_send(int fd) {
char buf[]="Hello Server!\n";
send(fd, buf, strlen(buf), 0);
printf("send msg: %s\n", buf);
close(fd);
}
int main(int argc, char *argv[]) {
int fd = socket_connect();
if (fd == -1) {
printf("client socket create failed\n");
return;
} else {
printf("socket connect success!, fd=%d\n", fd);
}
socket_send(fd);
return 0;
}
ps:
O_NONBLOCK和O_NDELAY所产生的结果都是使I/O变成非阻塞模式(non-blocking),在读取不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。
它们的差别在于:在读操作时,如果读不到数据,O_NDELAY会使I/O函数马上返回0,但这又衍生出一个问题,因为读取到文件末尾(EOF)时返回的也是0,这样无法区分是哪种情况。因此,O_NONBLOCK就产生出来,它在读取不到数据时会回传-1,并且设置errno为EAGAIN。
send,sendto以及sendmsg
socket编程读写文件recv()、send()函数返回值分析
ssize_t send (int s, const void *msg, size_t len, int flags);
ssize_t sendto (int s, const void *msg, size_t len, int flags, const struct sockaddr *to,
socklen_t tolen);
ssize_t sendmsg (int s, const struct msghdr *msg, int flags);
send,sendto以及sendmsg系统调用用于发送消息到另一个套接字。send函数在套接字处于连接状态时方可使用。而sendto和sendmsg在任何时候都可使用 。
下面的代码片段演示了send,sendto,sendmsg函数的用法:
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
void Send()
{
struct sockaddr_in serv_addr;
int sock_fd;
char line[15] = "Hello world!";
int size = 13;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(5000);
sock_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
connect(sock_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
send(sock_fd, line, size, 0);
close(sock_fd);
}
void Sendto()
{
sockaddr_in receiver_addr;
int sock_fd;
char line[15] = "Hello World!";
sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
receiver_addr.sin_family = AF_INET;
receiver_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
receiver_addr.sin_port = htons(5000);
sendto(sock_fd, line, 13, 0,(struct sockaddr*)&receiver_addr,sizeof(receiver_addr));
close(sock_fd);
}
void sendmsg()
{
struct sockaddr_in receiver_addr;
int sock_fd;
char line[15] = "Hello World!";
struct msghdr msg;
struct iovec iov;
sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
receiver_addr.sin_family = AF_INET;
receiver_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
receiver_addr.sin_port = htons(5000);
msg.msg_name = &receiver_addr;
msg.msg_namelen = sizeof(receiver_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base = line;
msg.msg_iov->iov_len = 13;
msg.msg_control = 0;
msg.msg_controllen = 0;
msg.msg_flags = 0;
sendmsg(sock_fd,&msg,0);
close(sock_fd);
}