LẬP TRÌNH SOCKET VỚI TCP/UDP

0.Một số khái niệm cần biết

Trước khi đi vào lập trình socket thì chúng ta cần quen với các khái niệm cơ bản trong lập trình socket.
a.Socket là gì?
-Socket là một cổng logic mà một chương trình sử dụng để kết nối với một chương trình khác chạy trên một máy tính khác trên Internet. Chương trình mạng có thể sử dụng nhiều Socket cùng một lúc, nhờ đó nhiều chương trình có thể sử dụng Internet cùng một lúc.
Có 2 loại Socket:
+Stream Socket: Dựa trên giao thức TCP( Tranmission Control Protocol) việc truyền dữ liệu chỉ thực hiện giữa 2 quá trình đã thiết lập kết nối. Giao thức này đảm bảo dữ liệu được truyền đến nơi nhận một cách đáng tin cậy, đúng thứ tự nhờ vào cơ chế quản lý luồng lưu thông trên mạng và cơ chế chống tắc nghẽn.
+Datagram Socket: Dựa trên giao thức UDP( User Datagram Protocol) việc truyền dữ liệu không yêu cầu có sự thiết lập kết nối giữa 2 quá trình. Ngược lại với giao thức TCP thì dữ liệu được truyền theo giao thức UDP không được tin cậy, có thế không đúng trình tự và lặp lại. Tuy nhiên vì nó không yêu cầu thiết lập kết nối không phải có những cơ chế phức tạp nên tốc độ nhanh…ứng dụng cho các ứng dụng truyền dữ liệu nhanh như chat, game…..
b.Port là gì ?
– Port xách định duy nhất một quá trình (process) trên một máy trong mạng. Hay nói cách khác là cách mà phân biệt giữa các ứng dụng.
VD: Khi máy bạn chạy nhiều ứng dụng mạng như Yahoo,Firefox, game online… .Ví dụ chương Yahoo sử dụng ( port 5150 hay 5050)  thì khi ai đó gửi tin nhắn đến cho bạn, lúc tin nhắn đến máy bạn nó sẽ dựa vào port để nhận biết đó là chương trình Yahoo ( port 5150) chứ ko pải là chương trình khác. Sau đó thông tin sẽ đc xử lý và hiễn thị tin nhắn lên.
– Một TCP/IP Socket gồm một địa chỉ IP kết hợp với một port ? Xác định duy nhất một tiến trình (process ) trên mạng.Hay nói cách khác Luồng thông tin trên mạng dựa vảo IP là để xác định máy một máy trên mạng còn port xác định 1 tiến trình trên 1 máy.
c.Ứng dụng Client – Server là gì
– Trước tới giờ, các bạn lập trình với mục đích là tạo ra được một ứng dụng. Nhưng ứng dụng đó chỉ hoạt động độc lập 1 mình riêng lẽ. Mục tiêu lập trình mạng sẽ đưa ra những ứng dụng dạng Client – Server. Tức là sẽ có 2 loại ứng dụng chính đó là Client và Server.
– Quy trình hoạt động của ứng dụng Server – Client như sau: Server có nhiệm vụ của là lắng nghe, chờ đợi kết nối từ Client trên địa chỉ IP của mình với PORT được quy định sẵn. Khi client gởi dữ liệu tới Server thì nó phải giải quyết một công việc là nhận dữ liệu đó -> xử lý -> trả kết quả lại cho Client.
– Client là ứng dụng được phục vụ, nó chỉ gởi truy vấn và chờ đợi kết quả từ Server

1.Cơ chế gọi hàm trong lập trình Socket

a.TCP

Lập trình Socket với TCP

b.UDP

Lập trình Socket với UDP

2.Thư viện lập trình Winsock

– WinSock API (Windows Sockets Application Programming Interface) là thư viện các hàm giao diện lập trình mạng cho Microsoft Windows. WinSock tương thích với họ nghi thức mạng TCP/IP.Ở phần này chúng ta sẽ làm quen với các hàm  để lập trình socket.

2.1. Khởi động và đóng thư viện

2.1.1. Khởi động Winsock

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

Các tham số:
– wVersionRequested là phiên bản thư viện mà mình sử dụng. Ở đây sẽ là giá trị 0x0202 có nghĩa là phiên bản 2.2.
– lpWSData là một số thông tin bổ sung sẽ được trả về sau khi gọi khởi tạo Winsock
2.1.2. Đóng thư viện Winsock

WSACleanup (void);

2.2. Window socket

2.2.1. Tạo socket

SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

Các tham số:
      – af: [in] mô tả họ địa chỉ.
      – type: [in] kiểu của socket.
              + SOCK_STREAM: TCP socket
              + SOCK_DGRAM: UDP socket
      -protocol: [in] nghi thức được sử dụng trên socket.
              + SOCK_DGREAM -> protocol là: IPPROTO_UDP
              + SOCK_STREAM -> protocol là: IPPROTO_IP
              +  SOCK_RAW -> protocol có thể là: IPPROTO_RAW hay IPPROTO_ICMP
2.2.2 Hàm lấy tên máy mình:

int gethostname(char* name, int namelen);

2.2.3Hàm lấy thông tin theo tên máy:

struct hostent* FAR gethostbyname(const char* name);

* Các tham số
        • name: [in] tên của máy tính cần phân giải.
* Giá trị trả về
       • Một cấu trúc HOSTENT, nếu thành công
       • NULL, nếu có lỗi
Trong đó hostent đc định nghĩa

typedef struct hostent {
  char FAR* h_name;            // Tên máy tính
  char FAR  FAR** h_aliases;        // Bí danh máy tính
  short h_addrtype;            // Kiểu IP (AF_INET)
  short h_length;                // Kích thước IP
  char FAR  FAR** h_addr_list;    // Danh sách các địa chỉ IP
                        // 1 host có thể có 1 hoặc nhiều IP
} HOSTENT,

2.2.4 Lấy thông tin khi biết địa chỉ IP

hostent* FAR gethostbyaddr(const char* addr, int len, int type);

Các tham số
       • addr: [in] địa chỉ của máy tính theo thứ tự network-byte.
       • len: [in] chiều dài của chuỗi địa chỉ
       • type: [in] kiểu của địa chỉ, được thiết lập là AF_INET.
* Giá trị trả về
   • Một cấu trúc HOSTENT, nếu thành công
       • NULL, nếu có lỗi

2.3. TCP

2.3.1. Gắn địa chỉ cho socket

int bind( SOCKET s, const struct sockaddr FAR* name, int namelen )

Các tham số
     • s: [in] socket chưa được gắn kết địa chỉ.
     • name: [in] địa chỉ được gán cho socket, một cấu trúc SOCKADDR.
     • namelen: [in] kích thước của giá trị tham số name.
* Giá trị trả về
     • 0, nếu thành công
     • SOCKET_ERROR, nếu có lỗi.
2.3.2. Lắng nghe kết nối

int listen( SOCKET s, int backlog );

* Các tham số
       • s: [in] socket đã được gắn địa chỉ nhưng chưa kết nối.
      • backlog: [in] kích thước tối đa của hàng đợi thiết lập kết nối. Giá trị tối đa được chỉ định bằng hằng số SOMAXCONN.
2.3.3. Chấp nhật thiết lập một kết nối

SOCKET accept( SOCKET s, struct sockaddr FAR* addr, int FAR*  addrlen )

* Các tham số
       • s: [in] socket đang lắng nghe yêu cầu kết nối.
       • addr: [out] địa chỉ của socket ở máy client đang thực hiện kết nối.
       • addrlen: [out] chiều dài thực sự của addr. Phải khởi tạo giá trị ban đầu là kích thước của addr
* Giá trị trả về
      • Một SOCKET để giao tiếp thực sự với client, nếu thành công
      • INVALID_SOCKET, nếu có lỗi
2.3.4. Thiết lập một kết nối

int connect( SOCKET s, const struct sockaddr FAR* name, int namelen )

* Các tham số
     • s: [in] socket chưa kết nối.
     • name: [in] socket cần kết nối đến.
     • namelen: [in] kích thước của name.
* Giá trị trả về
     • 0, nếu thành công
     • SOCKET_ERROR, nếu có lỗi
2.3.5. Gửi dữ liệu

int send( SOCKET s, const char FAR * buf, int len, int flags )

* Các tham số
     • s: [in] socket đã kết nối.
     • buf: [in] vùng đệm chứa dữ liệu cần gửi.
     • len: [in] chiều dài dữ liệu trong buf.
     • flags: [in] chỉ định cách thức truyền dữ liệu, truyền dữ liệu bình thường, thiết lập giá trị 0.
* Giá trị trả về
     • số byte đã gửi đi, nếu thành công
     • SOCKET_ERROR, nếu có lỗi.
2.3.6. Nhận dữ liệu

int recv( SOCKET s, char FAR* buf, int len, int flags )

* Các tham số
     • s: [in] socket đã kết nối.
     • buf: [out] vùng đệm để lưu dữ liệu nhận.
     • len: [in] kích thước vùng đệm buf.
     • flags: [in] chỉ định cách thức nhận dữ liệu, nhận dữ liệu bình thường, thiết lập giá trị 0.
* Giá trị trả về
     • số byte dữ liệu nhận được, nếu thành công
     • SOCKET_ERROR, nếu có lỗi.
2.3.7 Shutdown

int shutdown( SOCKET s, int how )

* Các tham số
      • s: [in] socket cần shutdown.
      • how: [in] chỉ định những loại thao tác nào không thực hiện nữa.
                  o SD_RECEIVE: không cho phép gọi các hàm recv() trên socket.
                 o SD_SEND: không cho phép gọi các hàm send() trên socket.
                 o SD_BOTH: không cho phép gọi cả send() và recv() trên socket.
* Giá trị trả về
      • 0, nếu thành công
     • SOCKET_ERROR, nếu có lỗi
2.3.7 Đóng socket

int closesocket (SOCKET s)

* Các tham số
     • s: [in] socket cần đóng.

3.Chương trình demo

Dưới đây là demo về chương trình chat giữa 2 máy trong mạng lan ứng dụng Server – Client sử dụng TCP và UDP. Các bạn có thể dowload project demo ở cuối bài.

Sever:

#include <stdio.h>
#include <tchar.h>
#include "iostream"
#include <winsock2.h>
using namespace std;
#pragma comment (lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA SInfo;
	int iResult = WSAStartup(0x0202,&SInfo);
	SOCKET NewConnection,socketSever;
	socketSever =socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
	if (socketSever!=INVALID_SOCKET)
	{
		cout<<"\nKhoi tao socket thanh cong!!!\n";
	}
	char DataGui[512];
	char DataNhan[512];
	//Thiet lap IP sever
	sockaddr_in severAddr;
	severAddr.sin_family=AF_INET;
	severAddr.sin_port= htons(2000);
	severAddr.sin_addr.s_addr= INADDR_ANY;
	//Thiet lap IP client
	if (iResult==0)
	{
		if (bind(socketSever,(sockaddr*)&severAddr,sizeof(severAddr))==0)
		{
			int nSize;
			//Lang nghe
			if (listen(socketSever,5)==SOCKET_ERROR	)
			{
				cout<<"\nLang nghe that bai";
			}
			else
				cout<<"Dang lang nghe ket noi...";//<<inet_ntoa(severAddr.sin_addr)<<" Port: "<<severAddr.sin_port;
			while(1)
			{
				nSize=sizeof(severAddr);
				NewConnection=accept(socketSever,(sockaddr*)&severAddr,&nSize);
				if (NewConnection==-1)
				{
					cout<<"\nLoi ket noi tu Client";
					continue;
				}
				cout<<"\nKet noi thanh cong..!";
				cout<<"\nNhan ket noi voi Client co IP: "<<inet_ntoa(severAddr.sin_addr);
				cout<<"\nBAT DAU NOI CHUYEN VOI NHAU";
				while(1)
				{
					recv(NewConnection,(char*) &DataNhan,sizeof(DataNhan),0);
					cout<<"\nClient: "<<DataNhan;
					cout<<"\nSever: ";
					cin.getline(DataGui,512);
					send(NewConnection,(char*) &DataGui,sizeof(DataGui),0);
				}
				closesocket(NewConnection);
			}
		}
		else
			cout<<"\nThiet lap IP va Port cho socket that bai";
		WSACleanup();
	}
	else
		cout<<"Loi khoi dong Winsock";
	system("PAUSE");
}

Client:

#include <stdio.h>
#include <tchar.h>
#include "iostream"
#include <winsock2.h>
using namespace std;
#pragma comment (lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA SInfo;
	int iResult = WSAStartup(0x0202,&SInfo);
	//Thiet lap IP client
	char DataGui[512];
	char DataNhan[512];
 	sockaddr_in clientAddr;
	char hostName[256];
	struct hostent* serverHostent;
	int port=2000;
	if (iResult==0)
	{
		SOCKET	socketClient =socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
		//Lay thong tin may chu qua ten
		cout<<"Enter host name : ";
		cin.getline(hostName,256);
		serverHostent=gethostbyname(hostName);
		if(serverHostent==NULL)
		{
			printf("Can not solve host \n");
		}
		memset(&clientAddr,0,sizeof(clientAddr));
		memcpy(&clientAddr.sin_addr,serverHostent->h_addr,serverHostent->h_length);
		clientAddr.sin_family=serverHostent->h_addrtype;
		clientAddr.sin_port=htons(port);
		//Yeu cau ket noi
		if (connect(socketClient,(sockaddr*)&clientAddr,sizeof(clientAddr))==0)
		{
			cout<<"\nKet noi thanh cong..!";
			cout<<"\nNhan ket noi voi may chu co IP: "<<inet_ntoa(clientAddr.sin_addr);
			cout<<"\nBAT DAU NOI CHUYEN VOI NHAU";
			while(1)
			{
				cout<<"\nClient: ";
				cin.getline(DataGui,512);
				send(socketClient,(char*)&DataGui,sizeof(DataGui),0);
				recv(socketClient,(char*)&DataNhan,sizeof(DataNhan),00);
				cout<<"\nSever: "<<DataNhan;
			}
		}
		else
			cout<<"Ket noi that bai";
		WSACleanup();
	}
	else
		cout<<"\nLoi khoi dong Winsock";
	system("PAUSE");
}

Download project demo 

Hy vọng bài viết sẽ giúp ích cho các bạn!
Thân!

Advertisements

4 comments

  1. bạn ơi, bạn có thể giúp mình dc ko, người ta yêu cầu mình làm 1 trò chơi tên là “đào kho báu” bao gồm 2 client và 1 đóng vai trò trung gian, mình có đính kèm file word

  2. Cảm ơn bạn !
    Bạn có sách nào về lập trình socket này không ? Hiện tại mình đang làm đồ án về các ứng dụng trong UDP, TCP mà không có tài liệu !
    thanks bạn nhiều.

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s